/* eslint-disable */
<template>
  <div class="graph-vis-content">
    <div class="header">
      <div class="left-header">
        <div class="header-text">知识图谱</div>
        <div class="split-line"></div>
        <load-data-modal
          :visible="loadDataModalVisible"
          :graph-id="graphId"
          :user-data-list="graphListData"
          :search-data-list="searchDataList"
          :btn-loading="confirmLoading"
          type="kg"
          @to-data-import="toDataImport"
          @key-change="onKeywordChanged"
          @render-gragh="renderGraph"
          @close="loadDataModalVisible = false"
          @confirm-import="onLoadDataModelOk"
        />
        <a-button
          v-if="!isLoadGraphData"
          class="load-button"
          type="primary"
          size="small"
          :disabled="isLock"
          @click="openLoadDataModel"
          >加载数据</a-button
        >
        <div v-else>
          <!-- <div class="graph-text">图谱数据：</div> -->
          <div class="graph-text selected-text">{{ selectedGraphName }}</div>
          <a-tooltip placement="top" :mouse-enter-delay="0.5">
            <template slot="title">
              <span>删除</span>
            </template>
            <div>
              <a-button
                type="link"
                class="delete-button"
                :disabled="isLock"
                @click="confirmDelete"
              >
                <a-icon-font
                  class="tool-icon"
                  type="iconicon-beifen5"
                ></a-icon-font>
              </a-button>
            </div>
          </a-tooltip>
        </div>
      </div>
      <div v-if="graphVisWidth < 450" class="toolbar">
        <a-tooltip placement="top" :mouse-enter-delay="0.5">
          <template slot="title">
            <span>工具栏</span>
          </template>
          <div>
            <a-button
              type="link"
              style="padding: 0;"
              :class="{
                'btn-select': showDetailMenu,
              }"
              @click="showDetailMenu = !showDetailMenu"
            >
              <a-icon-font
                class="tool-icon"
                type="iconxiangxixinxi"
              ></a-icon-font>
            </a-button>
          </div>
        </a-tooltip>
        <div
          v-if="showDetailMenu"
          class="detail-menu"
          :class="{ 'guest-mode': isLock }"
        >
          <div class="toolbar">
            <a-tooltip placement="top" :mouse-enter-delay="0.5">
              <template slot="title">
                <span>推送记录</span>
              </template>
              <div>
                <a-button
                  type="link"
                  style="padding: 0;"
                  disabled
                  @click="openPushRecord"
                >
                  <a-icon-font
                    class="tool-icon"
                    type="iconqingxi-caozuojilu"
                  ></a-icon-font>
                </a-button>
              </div>
            </a-tooltip>
            <a-tooltip placement="top" :mouse-enter-delay="0.5">
              <template slot="title">
                <span>推送数据</span>
              </template>
              <div>
                <a-button
                  type="link"
                  style="padding: 0;"
                  disabled
                  @click="pushData"
                >
                  <a-icon-font
                    class="tool-icon"
                    type="iconshujuguanli-daohang-daorushuju"
                  ></a-icon-font>
                </a-button>
              </div>
            </a-tooltip>
            <div class="split-line"></div>
            <a-dropdown
              :trigger="['click']"
              placement="bottomCenter"
              :mouse-enter-delay="0.5"
              :disabled="isLock"
            >
              <a-menu
                slot="overlay"
                :selected-keys="[selectedLayout]"
                style="padding: 0;"
              >
                <a-menu-item :key="KGraphMaps.GRID.alias">
                  <a-button type="link" style="padding: 0;" @click="gridLayout">
                    <a-icon-font
                      class="tool-icon"
                      type="iconicon-guanxitupu-buju1"
                    ></a-icon-font>
                  </a-button>
                </a-menu-item>
                <a-menu-item :key="KGraphMaps.DAGRE.alias">
                  <a-button type="link" style="padding: 0;" @click="dagreLayout">
                    <a-icon-font
                      class="tool-icon"
                      type="iconicon-buju2"
                    ></a-icon-font>
                  </a-button>
                </a-menu-item>
                <a-menu-item :key="KGraphMaps.FRUCHTERMAN.alias">
                  <a-button
                    type="link"
                    style="padding: 0;"
                    @click="fruchtermanLayout"
                  >
                    <a-icon-font
                      class="tool-icon"
                      type="iconguanxibuju3"
                    ></a-icon-font>
                  </a-button>
                </a-menu-item>
                <a-menu-item :key="KGraphMaps.RADIAL.alias">
                  <a-button
                    type="link"
                    style="padding: 0;"
                    @click="radialLayout"
                  >
                    <a-icon-font
                      class="tool-icon"
                      type="iconguanxibuju1-01"
                    ></a-icon-font>
                  </a-button>
                </a-menu-item>
              </a-menu>
              <div>
                <a-button type="link" style="padding: 0;" @click="dragMode">
                  <a-icon-font
                    class="tool-icon"
                    :type="
                      selectedLayout === KGraphMaps.GRID.alias
                        ? 'iconicon-guanxitupu-buju1'
                        : selectedLayout === KGraphMaps.DAGRE.alias
                        ? 'iconicon-buju2'
                        : selectedLayout === KGraphMaps.FRUCHTERMAN.alias
                        ? 'iconguanxibuju3'
                        : 'iconguanxibuju1-01'
                    "
                  ></a-icon-font>
                </a-button>
              </div>
            </a-dropdown>
            <filter-items
              ref="filter-items-toolbar"
              :disabled="isLock"
              :filter-content-visible="filterContentVisible"
              :checked-class-keys="checkedClassKeys"
              :checked-relation-keys="checkedRelationKeys"
              :graph-data="rawData"
              @on-check-filter-class="onCheckFilterClass"
              @on-check-filter-relation="onCheckFilterRelation"
              @on-filter-items="onFilterItems"
              @open-filter-content="openFilterContent"
              @close-filter-content="closeFilterContent"
            ></filter-items>
            <!-- <a-tooltip placement="top" :mouse-enter-delay="0.5">
              <template slot="title">
                <span>拖动</span>
              </template>
              <div>
                <a-button type="link" style="padding: 0;" @click="dragMode">
                  <a-icon-font
                    class="tool-icon"
                    style="color: #1ff;"
                    :type="
                      selectedMode === 'default'
                        ? 'icontuodongbeifen'
                        : 'icontuodongbeifen'
                    "
                  ></a-icon-font>
                </a-button>
              </div>
            </a-tooltip> -->
            <a-tooltip placement="top">
              <template slot="title">
                <span>初始化</span>
              </template>
              <div>
                <a-button type="link" style="padding: 0;" @click="resetLayout">
                  <a-icon-font
                    class="tool-icon"
                    type="iconhuidaohuabanbeifen3"
                  />
                </a-button>
              </div>
            </a-tooltip>
            <a-tooltip placement="top" :mouse-enter-delay="0.5">
              <template slot="title">
                <span>搜索</span>
              </template>
              <div>
                <a-dropdown
                  :visible="searchInputVisible"
                  :class="{ 'btn-select': searchInputVisible }"
                  placement="bottomLeft"
                  :mouse-enter-delay="0.5"
                  :disabled="isLock"
                >
                  <div slot="overlay" class="vis-search">
                    <a-input
                      v-model="searchInput"
                      placeholder="请输入实体名称"
                      style="width: 200px;"
                      @pressEnter="onSearchInput"
                    >
                      <template #suffix>
                        <a-button
                          type="link"
                          style="padding: 0 10px;"
                          @click="onSearchInput"
                        >
                          <a-icon-font
                            class="tool-icon"
                            type="iconsousuo"
                          ></a-icon-font
                        ></a-button> </template
                    ></a-input>
                    <div
                      class="close-icon"
                      @click="
                        () => {
                          searchInputVisible = false
                        }
                      "
                    >
                      <a-icon-font type="iconicon-beifen1" />
                    </div>
                  </div>
                  <a-button
                    type="link"
                    style="padding: 0;"
                    @click="openSearchInput"
                  >
                    <a-icon-font
                      class="tool-icon"
                      type="iconsousuo"
                    ></a-icon-font>
                  </a-button>
                </a-dropdown>
              </div>
            </a-tooltip>
          </div>
        </div>
      </div>
      <div
        v-if="graphVisWidth >= 450 || dropDownToolBar"
        :class="{
          toolbar: true,
          dropDownToolBar: dropDownToolBar && graphVisWidth < 450,
          'guest-mode': isLock,
        }"
      >
        <a-tooltip placement="top" :mouse-enter-delay="0.5">
          <template slot="title">
            <span>推送记录</span>
          </template>
          <div>
            <a-button
              type="link"
              style="padding: 0;"
              disabled
              @click="openPushRecord"
            >
              <a-icon-font
                class="tool-icon"
                type="iconqingxi-caozuojilu"
              ></a-icon-font>
            </a-button>
          </div>
        </a-tooltip>
        <a-tooltip placement="top" :mouse-enter-delay="0.5">
          <template slot="title">
            <span>推送数据</span>
          </template>
          <div>
            <a-button type="link" style="padding: 0;" disabled @click="pushData">
              <a-icon-font
                class="tool-icon"
                type="iconshujuguanli-daohang-daorushuju"
              ></a-icon-font>
            </a-button>
          </div>
        </a-tooltip>
        <div class="split-line"></div>
        <a-dropdown
          :trigger="['click']"
          placement="bottomCenter"
          :mouse-enter-delay="0.5"
          :disabled="isLock"
        >
          <a-menu
            slot="overlay"
            :selected-keys="[selectedLayout]"
            style="padding: 0;"
          >
            <a-menu-item :key="KGraphMaps.GRID.alias">
              <a-button type="link" style="padding: 0;" @click="gridLayout">
                <a-icon-font
                  class="tool-icon"
                  type="iconicon-guanxitupu-buju1"
                ></a-icon-font>
              </a-button>
            </a-menu-item>
            <a-menu-item :key="KGraphMaps.DAGRE.alias">
              <a-button type="link" style="padding: 0;" @click="dagreLayout">
                <a-icon-font
                  class="tool-icon"
                  type="iconicon-buju2"
                ></a-icon-font>
              </a-button>
            </a-menu-item>
            <a-menu-item :key="KGraphMaps.FRUCHTERMAN.alias">
              <a-button
                type="link"
                style="padding: 0;"
                @click="fruchtermanLayout"
              >
                <a-icon-font
                  class="tool-icon"
                  type="iconguanxibuju3"
                ></a-icon-font>
              </a-button>
            </a-menu-item>
            <a-menu-item :key="KGraphMaps.RADIAL.alias">
              <a-button type="link" style="padding: 0;" @click="radialLayout">
                <a-icon-font
                  class="tool-icon"
                  type="iconguanxibuju1-01"
                ></a-icon-font>
              </a-button>
            </a-menu-item>
          </a-menu>
          <div>
            <a-button type="link" style="padding: 0;" @click="dragMode">
              <a-icon-font
                class="tool-icon"
                :type="
                  selectedLayout === KGraphMaps.GRID.alias
                    ? 'iconicon-guanxitupu-buju1'
                    : selectedLayout === KGraphMaps.DAGRE.alias
                    ? 'iconicon-buju2'
                    : selectedLayout === KGraphMaps.FRUCHTERMAN.alias
                    ? 'iconguanxibuju3'
                    : 'iconguanxibuju1-01'
                "
              ></a-icon-font>
            </a-button>
          </div>
        </a-dropdown>
        <filter-items
          ref="filter-items"
          :disabled="isLock"
          :filter-content-visible="filterContentVisible"
          :checked-class-keys="checkedClassKeys"
          :checked-relation-keys="checkedRelationKeys"
          :graph-data="rawData"
          @on-check-filter-class="onCheckFilterClass"
          @on-check-filter-relation="onCheckFilterRelation"
          @on-filter-items="onFilterItems"
          @open-filter-content="openFilterContent"
          @close-filter-content="closeFilterContent"
        ></filter-items>
        <!-- <a-tooltip placement="top" :mouse-enter-delay="0.5">
          <template slot="title">
            <span>拖动</span>
          </template>
          <div>
            <a-button type="link" style="padding: 0;" @click="dragMode">
              <a-icon-font
                class="tool-icon"
                style="color: #1ff;"
                :type="
                  selectedMode === 'default'
                    ? 'icontuodongbeifen'
                    : 'icontuodongbeifen'
                "
              ></a-icon-font>
            </a-button>
          </div>
        </a-tooltip> -->
        <!-- <a-tooltip placement="top" :mouse-enter-delay="0.5">
          <template slot="title">
            <span>框选</span>
          </template>
          <div>
            <a-button type="link" style="padding: 0;" @click="brushMode">
              <a-icon-font
                class="tool-icon"
                :type="
                  selectedMode === 'brush'
                    ? 'iconkuangxuanbeifen-copy'
                    : 'iconkuangxuanbeifen'
                "
              ></a-icon-font>
            </a-button>
          </div>
        </a-tooltip> -->
        <a-tooltip placement="top">
          <template slot="title">
            <span>初始化</span>
          </template>
          <div>
            <a-button type="link" style="padding: 0;" @click="resetLayout">
              <a-icon-font class="tool-icon" type="iconhuidaohuabanbeifen3" />
            </a-button>
          </div>
        </a-tooltip>
        <a-tooltip placement="top" :mouse-enter-delay="0.5">
          <template slot="title">
            <span>搜索</span>
          </template>
          <div>
            <a-dropdown
              :visible="searchInputVisible"
              :class="{ 'btn-select': searchInputVisible }"
              placement="bottomLeft"
              :disabled="isLock"
              :mouse-enter-delay="0.5"
            >
              <div slot="overlay" class="vis-search">
                <a-input
                  v-model="searchInput"
                  placeholder="请输入实体名称"
                  style="width: 200px;"
                  @pressEnter="onSearchInput"
                >
                  <template #suffix>
                    <a-button
                      type="link"
                      style="padding: 0 10px;"
                      @click="onSearchInput"
                    >
                      <a-icon-font
                        class="tool-icon"
                        type="iconsousuo"
                      ></a-icon-font
                    ></a-button> </template
                ></a-input>
                <div
                  class="close-icon"
                  @click="
                    () => {
                      searchInputVisible = false
                    }
                  "
                >
                  <a-icon-font type="iconicon-beifen1" />
                </div>
              </div>
              <a-button type="link" style="padding: 0;" @click="openSearchInput">
                <a-icon-font class="tool-icon" type="iconsousuo"></a-icon-font>
              </a-button>
            </a-dropdown>
          </div>
        </a-tooltip>
      </div>
    </div>
    <div>
      <div
        id="graph-vis"
        :class="nodeEditPanelVisible ? 'node-selected-graph-vis' : 'graph-vis'"
      >
        <a-spin
          v-if="isGraphVisLoading"
          size="large"
          tip="Loading..."
          :style="{
            width: `${graphVisWidth}px`,
            height: '100%',
            display: 'flex',
            'justify-content': 'center',
            'flex-direction': 'column',
            'align-items': 'center',
          }"
        ></a-spin>
        <div
          v-if="!isLoadGraphData"
          :style="{
            width: `${graphVisWidth}px`,
            height: '100%',
            display: 'flex',
            'justify-content': 'center',
            'flex-direction': 'column',
            'align-items': 'center',
          }"
        >
          <img :src="require(`@/assets/no-kgraph-loaded.png`)" />
          当前无图谱数据<br />
          请点击上方按钮加载图谱
        </div>
      </div>
    </div>
    <div v-if="nodeEditPanelVisible" class="node-edit-panel">
      <div v-if="selectedNodeModel.isClass === -1" class="gis-panel">
        <div class="node-edit-header left-header">
          <div>{{ selectedNodeModel.label }}</div>
        </div>
        <!-- 仅gis 实体节点展示, 这里要有条件过滤TODO -->
        <simple-k-gis-map
          :id="'gis-node-map'"
          ref="knowledgeGisMap"
          :layer-list="layerList"
          :gis-info="gisInfo"
        ></simple-k-gis-map>
      </div>
      <div v-else class="not-gis-panel">
        <div :style="{ width: `${nodePanelWidth}px` }">
          <template v-if="collapseLeftPanel === false">
            <div class="node-edit-header left-header">
              <div>基础信息</div>
              <a-icon
                type="left"
                style="margin: auto 0;"
                @click="
                  () => {
                    collapseLeftPanel = true
                    nodePanelWidth = 44
                  }
                "
              ></a-icon>
            </div>
            <div class="node-edit-body">
              <template v-for="(item, index) in selectedNodeBasic.text">
                <div
                  v-if="['email', 'link'].includes(item.type)"
                  :key="
                    item.type === 'email' ? `email-${index}` : `link-${index}`
                  "
                  class="node-panel-text-warpper"
                >
                  <div class="node-panel-text-name" :style="textNameStyle">
                    {{ item.attrName }}:
                  </div>
                  <a
                    class="entity-link"
                    :href="
                      item.type === 'email'
                        ? `mailto:${item.attrValue}`
                        : item.attrValue
                    "
                    :title="item.attrValue"
                    target="_blank"
                  >
                    {{ item.attrValue }}
                  </a>
                </div>
                <div
                  v-else
                  :key="`text-${index}`"
                  class="node-panel-text-warpper"
                >
                  <div class="node-panel-text-name" :style="textNameStyle">
                    {{ item.attrName }}:
                  </div>
                  <div
                    class="wrapper"
                    :class="{
                      'hide-btn':
                        item.attrValue.length < 30 ||
                        item.attrValue.includes('http'),
                    }"
                  >
                    <input :id="index" class="exp" type="checkbox" />
                    <div class="text">
                      <label class="btn" :for="index"></label>

                      <span
                        class="node-panel-text-value"
                        v-html="item.attrValue"
                      ></span>
                    </div>
                  </div>
                  <!-- <div class="node-panel-text-value" v-html="item.attrValue"> -->
                  <!-- {{ item.attrValue }} -->
                  <!-- </div> -->
                </div>
              </template>
              <template v-for="(item, index) in selectedNodeBasic.image">
                <div :key="`image-${index}`">
                  <div style="item-option">{{ item.attrName }}:</div>
                  <div class="media-container">
                    <img :src="item.attrValue" />
                  </div>
                </div>
              </template>
            </div>
          </template>
          <div v-else class="left-header-fold" style="height: 400px;">
            <div style="letter-spacing: 8px; padding: 10px 0;">基础信息</div>
            <a-icon
              type="right"
              @click="
                () => {
                  collapseLeftPanel = false
                  nodePanelWidth = 200
                }
              "
            ></a-icon>
          </div>
        </div>
        <div
          id="node-panel-resize-line"
          class="node-panel-resize-line"
          :style="{ left: `${nodePanelWidth - 5}px` }"
        />
        <div
          class="graph-vis-right-panel"
          :style="{
            width: `calc(${graphVisWidth}px - ${nodePanelWidth}px)`,
          }"
        >
          <div class="node-edit-header right-header">
            <div class="node-edit-title">{{ selectedNodeModel.label }}</div>
            <a-button
              class="add-vis-button"
              type="primary"
              size="small"
              :disabled="true"
              @click="openAddVisualiztionModal()"
              >添加可视化</a-button
            >
          </div>
          <div class="node-edit-body">
            <div style="height: 30px; white-space: nowrap;">
              <span v-if="nodeEditTableList.length !== 0">
                <span
                  class="default-item"
                  :class="{
                    'select-item': nodeEditTableIndex === -1,
                  }"
                  @click="
                    () => {
                      nodeEditTableIndex = -1
                      getTableData('ALL')
                    }
                  "
                  >可视化</span
                >
                <span> | </span>
              </span>
              <span
                v-for="(item, index) in nodeEditTableList"
                :key="`table-${index}`"
              >
                <span
                  class="default-item"
                  :class="{
                    'select-item': nodeEditTableIndex === index,
                  }"
                  @click="
                    () => {
                      nodeEditTableIndex = index
                      getTableData(item.attrValue)
                    }
                  "
                  >{{ item.attrName }}</span
                >
                <span> | </span>
              </span>
            </div>
            <div style="display: flex; flex-flow: wrap; padding: 10px;">
              <div
                v-for="widget in widgets"
                :key="widget.id"
                style="width: 200px;"
              >
                <div class="widget-item">
                  <img
                    alt=""
                    :src="`${publicPath}${widget.imgSrc}`"
                    :style="{
                      height: '100%',
                      width: '100%',
                      backgroundColor: '#fff',
                    }"
                  />
                  <div class="hover-visible">
                    <div class="widget-name">
                      {{ widget.data.chartOptions.title }}
                    </div>
                    <!--删除icon-->
                    <div
                      v-if="widget.id !== null && !isVisitor"
                      class="delete-icon"
                    >
                      <a-tooltip placement="top">
                        <template slot="title">
                          <span>删除</span>
                        </template>
                        <a-icon-font
                          v-if="!isLock"
                          type="iconicon-beifen5-copy"
                          class="little-icon"
                          @click="onDelete(widget)"
                        />
                      </a-tooltip>
                    </div>
                    <!--编辑icon-->
                    <div
                      v-if="widget.id !== null && !isVisitor"
                      class="edit-icon"
                    >
                      <a-tooltip placement="top">
                        <template slot="title">
                          <span>编辑</span>
                        </template>
                        <a-icon-font
                          v-if="!isLock"
                          type="iconicon-beifen6-copy"
                          class="little-icon"
                          @click="editMyVisualization(widget, 'dataset')"
                        />
                      </a-tooltip>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div v-if="nodeEditTableIndex !== -1" class="table-container">
              <div v-if="tableIsLoading" class="loading-mask">
                <div class="loading-status">
                  <a-icon class="sync-icon" type="sync" spin /><span
                    class="loading-text"
                    >加载中...</span
                  >
                </div>
              </div>
              <img
                v-if="nodeTableData.length === 0 && !tableIsLoading"
                class="no-data-img"
                alt="empty"
                src="@/assets/view/empty2.png"
              />
              <simple-virtual-table
                v-else
                ref="virtualTable"
                :data-column="nodeTableColumns"
                :data-sources="nodeTableData"
                @table-body-scroll="onTableScroll"
              >
                <template #headerBefore>
                  <table-column
                    key="xuhao"
                    class="header-item data-cell"
                    :width="70"
                    fixed="left"
                  >
                    序号
                  </table-column>
                </template>
                <template #header="{ column }">
                  <span class="data-cell">{{ column.name }}</span>
                </template>

                <template #rowBefore="{ index }">
                  <table-column :width="70" class="data-cell" fixed="left">
                    <span :index="index">{{ index + 1 }}</span>
                  </table-column>
                </template>

                <template #cell="{ column, row, index: $index }">
                  <div style="height: 100%;">
                    <span
                      :index="$index"
                      :class="{
                        'data-cell': true,
                      }"
                    >
                      <!-- 这一层用于触发刷选,请确保内容最后包含一个空格 -->
                      <span class="cell-hilight" :title="row[column.name]"
                        >{{ row[column.name] }}
                      </span>
                    </span>
                  </div>
                </template>
              </simple-virtual-table>
            </div>
          </div>
        </div>
      </div>
    </div>

    <modal-visualization
      :chart-config="myModalVisualizationConfig.chartConfig"
      :dataset-list="myModalVisualizationConfig.datasetList"
      :visible="myModalVisualizationConfig.visible"
      @cancel="myModalVisualizationConfig.visible = false"
      @ok="onMyModalVisualizationSave"
    >
    </modal-visualization>
    <push-data-modal
      :visible="pushDataModalConfig.visible"
      @cancel="pushDataModalConfig.visible = false"
      @ok="onSave"
    >
    </push-data-modal>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch, Prop } from 'vue-property-decorator'
import PushDataModal from '@/components/common/GraphVisualization/PushDataModal.vue'
import ModalVisualization from '@/components/common/VisualizationDashboard/ModalVisualization.vue'
// import elementResizeDetectorMaker from 'element-resize-detector'
import { Modal } from 'ant-design-vue'
import G6 from '@antv/g6'
import {
  queryKnowledgeGraph,
  searchKnowledgeGraph,
  expandVertice,
  collapseVertice,
  // queryByUserId,
  bindProject,
  unbindProject,
  queryPanelInfoByVid,
  updatePanelInfoByVid,
  queryByProjectId,
  getData,
} from '@/api/knowledge-graph'
import {
  // widgetQueryByProject,
  deleteWidget,
  saveWidget,
  updateWidget,
  getWidgetData,
} from '@/api/widget'
import { queryUserDatalist } from '@/api/data-list'
import SimpleKGisMap from '@/components/knowledge-gis/SimpleKGisMap.vue'
import { GisInfo } from '@/interface/k-graph-interface'
import KGraphStore from '@/store/modules/k-graph'
// import DataViewStore from '@/store/modules/dataview'
import { debounce, cloneDeep } from 'lodash'
import { publicPath } from '@/api/constants'
// import UserStore from '@/store/modules/user'
import { chartMaps, KGraphMaps } from '@/config/contant'
import FilterItems from '@/components/knowledge-vis/FilterItems.vue'
import SimpleVirtualTable from '@/components/simple-virtual-table/Table.vue'
import TableColumn from '@/components/simple-virtual-table/TableColumn.vue'
import { parseGetDataResult } from '@/components/common/VisualizationDashboard/util'
import LoadDataModal from '@/components/Modal/LoadDataModal.vue'
import { shapePrefixInfo } from '@/util/prefix-utils'

const fittingString = (input: string, maxWidth: any, fontSize: any) => {
  const ellipsis = '...'
  const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]
  let currentWidth = 0
  let result = input
  const pattern = new RegExp('[\u4E00-\u9FA5]+') // distinguish the Chinese charactors and letters
  input.split('').forEach((letter: string, i) => {
    if (currentWidth > maxWidth - ellipsisLength) {
      return
    }
    if (pattern.test(letter)) {
      // Chinese charactors
      currentWidth += fontSize
    } else {
      // get the width of single letter according to the fontSize
      currentWidth += G6.Util.getLetterWidth(letter, fontSize)
    }
    if (currentWidth > maxWidth - ellipsisLength) {
      result = `${input.slice(0, i)}${ellipsis}`
    }
  })
  return result
}

// rowSelection onChange不会改变this里的值
let selectedRows: any

/** 根节点颜色 */
const colorRoot = {
  concept: '#9E9D9D',
  entity: '#9E9D9D',
  conceptStroke: '#000000',
  entityStroke: '#000000',
}
/** 颜色集合：按照概念顺序分配随机颜色,concept是概念颜色、entity是对应的实体颜色 */
const colors = [
  {
    concept: '#7A88F2',
    entity: '#BDC4FA',
    conceptStroke: '#1C35EE',
    entityStroke: '#7A88F2',
  },
  {
    concept: '#FBC94B',
    entity: '#FFE4A3',
    conceptStroke: '#B98F0F',
    entityStroke: '#FBD477',
  },
  {
    concept: '#65DCB8',
    entity: '#B9E5D8',
    conceptStroke: '#29A881',
    entityStroke: '#25D7A3',
  },
  {
    concept: '#EF7C52',
    entity: '#FBDED3',
    conceptStroke: '#ED5721',
    entityStroke: '#FBC1AB',
  },
  {
    concept: '#F595C6',
    entity: '#FACAE2',
    conceptStroke: '#E478AF',
    entityStroke: '#F99ECB',
  },
  {
    concept: '#6E80A2',
    entity: '#B0B8C6',
    conceptStroke: '#273D67',
    entityStroke: '#6E80A2',
  },
  {
    concept: '#5DC6D7',
    entity: '#B7E1E7',
    conceptStroke: '#2E8B9A',
    entityStroke: '#5DC6D7',
  },
  {
    concept: '#FFA65E',
    entity: '#FFD2AE',
    conceptStroke: '#DC7929',
    entityStroke: '#FFBA83',
  },
  {
    concept: '#ABABAB',
    entity: '#DFDFDF',
    conceptStroke: '#7C7B7B',
    entityStroke: '#979797',
  },
  {
    concept: '#CC69D9',
    entity: '#EBB2F2',
    conceptStroke: '#9D40A9',
    entityStroke: '#E38AEE',
  },
  {
    concept: '#16A37E',
    entity: '#8CCEBC',
    conceptStroke: '#11775C',
    entityStroke: '#16A37E',
  },
  {
    concept: '#7751FF',
    entity: '#C3B2FF',
    conceptStroke: '#38218B',
    entityStroke: '#7751FF',
  },
]

/** 节点、边的阴影值 */
const ShadowBlur = {
  INIT: 0,
  HIGH: 10,
}

@Component({
  components: {
    PushDataModal,
    ModalVisualization,
    SimpleKGisMap,
    FilterItems,
    SimpleVirtualTable,
    TableColumn,
    LoadDataModal,
  },
})
export default class GraphVisPanel extends Vue {
  @Prop() graphVisWidth: any
  pushDataModalConfig: {
    visible: boolean
  } = {
    visible: false,
  }
  public myModalVisualizationConfig: {
    visible: boolean
    chartConfig: any
    datasetList: {
      dataset: Array<any>
      pipeline: Array<any>
    }
    widgetId?: number
  } = {
    visible: false,
    chartConfig: {},
    datasetList: {
      dataset: [],
      pipeline: [],
    },
  }

  public tableIsLoading: boolean = false
  private publicPath = publicPath // 基础URL
  private chartMaps: any = chartMaps // charts映射
  // 加载图谱列表
  public loadDataModalVisible = false
  // 过滤
  public filterContentVisible = false
  // 搜索
  public searchInputVisible = false
  public collapseLeftPanel = false
  public isLoadGraphData = false
  // public checkedClassKeys: string[] = []
  // public checkedRelationKeys: string[] = []
  public selectedGraphName = ''
  // public graphId = -1
  public nodeEditPanelVisible = false
  public selectedNodeModel: any
  public selectedNodeBasic: any = {}
  public selectedNodeData: any = {}
  // public rawData: any = {}
  public graphData: any
  public selectedRows: any
  private graph: any // 画布
  private selectedMode: any = 'default'
  private selectedLayout: any = KGraphMaps.DAGRE.alias
  private nodePanelWidth: number = 200
  private searchInput = ''
  private projectId: number = 0
  public searchText = ''
  public searchedColumn = 0
  public filterClassData: any = []
  public filterRelationData: any = []
  public graphListData: any = []
  public nodeEditTableList: any = []
  public chartsGallery: any = []
  public nodeEditTableIndex: any = -1
  public nodeTableColumns: any = []
  public nodeTableData: any = []
  private rightClickDict: any = {}
  private currentPage: number = 1
  private totalPages: number = 0
  private isNodeTableLoading: boolean = false
  private isGraphVisLoading: boolean = false
  private dropDownToolBar: boolean = false
  public graphListColumns: any = [
    {
      title: '图谱名称',
      dataIndex: 'name',
      customFilterDropdown: true,
      onFilter: (value: any, record: any) => {
        record.address.toString().toLowerCase().includes(value.toLowerCase())
      },
    },
    {
      title: '发布时间',
      dataIndex: 'gmtModify',
      sorter: {
        compare: (a: any, b: any) => a - b,
      },
      customFilterDropdown: true,
      onFilter: (value: any, record: any) => {
        record.address.toString().toLowerCase().includes(value.toLowerCase())
      },
    },
  ]

  public searchInit: boolean = false //  由search 发起的query

  public widgets: any = []

  public showDetailMenu: boolean = false //  是否显示详细菜单

  public confirmLoading: boolean = false //  loading status

  public searchDataList: Array<any> = [] // 加载数据搜索结果

  // 项目锁定
  public get isLock() {
    // return false
    return KGraphStore.getLock
  }

  public get isVisitor() {
    //  访客身份
    return KGraphStore.isProjectGuest
  }

  public get checkedClassKeys() {
    return KGraphStore.checkedClassKeys
  }

  public get checkedRelationKeys() {
    return KGraphStore.checkedRelationKeys
  }

  public get rawData() {
    return KGraphStore.filterRawData
  }

  public get sourceData() {
    return KGraphStore.sourceData
  }

  public get graphId() {
    return KGraphStore.graphId
  }

  public get reloadGraph() {
    return KGraphStore.reloadGraph
  }

  // 根据面板宽度，动态调整属性列所占的宽度
  public get textNameStyle() {
    return {
      width:
        this.nodePanelWidth === null || this.nodePanelWidth <= 220
          ? '65px'
          : '120px',
    }
  }

  public get baseUri() {
    return KGraphStore.baseUri
  }

  private width: number = 0
  private height: number = 0
  // 迭代布局完成之后，存储数据信息（包括位置信息）,在新增/展开、移除/折叠节点时需要位置信息
  public graphDataAfterLayout: any
  // 布局、布局参数等常量信息
  public KGraphMaps: any = KGraphMaps
  // 根据分类随机设置颜色
  public colorSet: any = {}

  @Watch('reloadGraph', { deep: true })
  handleReloadChange(newValue: any) {
    if (newValue) {
      this.graph.clear()
      this.graphData = this.preprocessData(newValue)
      this.rightClickDict = {}
      this.graph.changeData(this.graphData)
    }
  }

  //  获取当前置顶的面板id
  public get selectEntityPanel() {
    return KGraphStore.selectEntityPanel
  }

  @Watch('selectEntityPanel')
  public handleEntityPanel(newValue: number) {
    //  配置项允许联动则开启
    if (this.interactGraph) {
      //  关闭基础信息面板
      this.nodeEditPanelVisible = false
      const id = newValue.toString()
      // zoom到选中节点
      this.zoomToTargetNode(id, 0)
      this.handleHighLight(this.graphData, id)
    }
  }

  /**
   * 高亮当前操作节点，关闭其他节点的高亮、以及边的高亮
   * @param graphData
   * @param id
   */
  public handleHighLight(graphData: any, id: string) {
    graphData.nodes?.forEach((node: any) => {
      const instance = this.graph.findById(node.id)
      if (node.id === id) {
        // 高亮展示
        instance?.setState('selected', true)
      } else {
        // 关掉高亮
        instance?.setState('selected', false)
      }
    })
    graphData.edges?.forEach((edge: any) => {
      const instance = this.graph.findById(edge.id)
      if (edge.id === id) {
        // 高亮展示
        instance?.setState('selected', true)
      } else {
        // 关掉高亮
        instance?.setState('selected', false)
      }
    })
  }

  public get interactGraph() {
    return KGraphStore.sysSetting.interactGraph
  }

  // 选中行
  public rowSelection = {
    selectedRows,
    onChange: (selectedRowKeys: string[], newSelectedRows: any) => {
      selectedRows = newSelectedRows
    },
    hideSelectAll: true,
    type: 'radio',
  }

  public beforeMount() {}

  // 2022-10-14
  // 后续优化：
  // 1、后端返回数据，除了id加前缀以外，其他的可以把前缀去掉（可以搜索shapePrefixInfo、removePrefix、baseUri）
  // 2、请求方式优化(可以搜索formData)
  // 3、请求数据base64加密的，可以优化为encodeUriComponent即可(可以搜索window.btoa)
  public mounted() {
    this.projectId = Number(this.$route.params.id)
    // 后端接口一直提示需要Long型参数，先用formData传参
    const formData = new FormData()
    formData.append('projectId', this.$route.params.id)
    queryByProjectId({ data: formData }).then((response: any) => {
      const { code, data } = response.data
      if (code === 200 && data[0]) {
        // this.graphId = response.data.result.id
        const { info, baseUri, id, filename } = data[0]
        const infoShape = shapePrefixInfo(info, baseUri)
        console.log('infoShape:', infoShape)
        this.initFilterCheckedItem(infoShape)
        KGraphStore.setSourceData(this.preprocessData(infoShape))
        KGraphStore.setGraphId(id)
        KGraphStore.setBaseUri(baseUri)
        this.initGraph(this.sourceData.filterItems.class)
        this.selectedGraphName = filename
        this.isLoadGraphData = true
        KGraphStore.setUpdateDashboardTime()
        this.renderGraph()
      }
    })
  }

  //  有初始化的filter check
  private withInitFilter: boolean = false
  //  初始化checked 的filter 选项
  initFilterCheckedItem(info: any) {
    //  set filter checked edge
    if (info?.checkedFilterItems) {
      console.log('initFilterCheckedItem-info:', info)
      this.withInitFilter = true
      const { checkedFilterItems } = info
      const classFilter = checkedFilterItems.class
      const edgeFilter = checkedFilterItems.relation
      KGraphStore.setRelationKeys(edgeFilter)
      const parsedClassFilter = Array.isArray(classFilter)
        ? classFilter
        : JSON.parse(classFilter)
      // console.log(parsedClassFilter, 'parsedClassFilter')
      // console.log(edgeFilter, 'edgeFilter')
      if (Array.isArray(classFilter)) {
        //  set filter checked class
        KGraphStore.setClassKeys(parsedClassFilter)
      } else {
        const result: any = []
        Object.keys(parsedClassFilter).forEach((key) => {
          if (!result.includes(key)) {
            result.push(key)
          }
          if (parsedClassFilter[key] !== undefined) {
            parsedClassFilter[key].forEach((item: any) => {
              result.push(item)
            })
          }
        })

        //  set filter checked class
        KGraphStore.setClassKeys(result)
      }
    }
  }

  /**
   *  跳转到数据导入  cankao
   */
  public toDataImport() {
    // 保存当前的url
    // const parameters: any = {
    //   url: `/project/${this.$route.params.id}/gis`,
    //   projectId: this.$route.params.id,
    //   loadType: 'kg',
    // }
    // sessionStorage.setItem('lastUrl', JSON.stringify(parameters))
    // const { href } = this.$router.resolve({
    //   path: '/management/data/import',
    // })
    // window.location.href = href
  }

  /**
   * 关键字搜索
   */
  public async onKeywordChanged(keyword: string, type: any) {
    const response = await queryUserDatalist({
      // const response = await searchUserDatalist({
      data: {
        content: keyword.trim(),
        projectId: this.projectId,
        type,
      },
    })
    const list: any = []
    response.data.result.forEach((group: any) => {
      group.dataset.forEach((item: any) => {
        list.push({
          title: `${item.name} (${group.categoryName})`,
          key: item.id,
          disableCheckbox: item.status === 1,
        })
      })
    })
    this.searchDataList = list
  }

  // 所有图层
  public get layerList() {
    //  TODO 后续跟进source: gis 筛选出gis 自带的图层
    // return KGraphStore.layerList
    return KGraphStore.layerList.filter((layer: any) => {
      return layer.source !== 'graph'
    })
  }
  // dashboard gis info
  public get gisInfo(): GisInfo | null {
    return KGraphStore.gisInfo
  }

  public changeGraphsize = debounce(() => {
    this._changeGraphsize()
  }, 500)

  public _changeGraphsize() {
    this.graph.changeSize(this.graphVisWidth, this.graph.cfg.height)

    const table = this.$refs.virtualTable as any
    if (table) {
      table.resize()
    }
    //  隐藏toolbar时，init filter & set showDetailMenu = false
    if (this.graphVisWidth >= 450) {
      this.$nextTick(() => {
        const filterItemsTool = this.$refs['filter-items'] as FilterItems
        filterItemsTool?.initFilterItems()
      })

      this.showDetailMenu = false
    }
  }

  @Watch('showDetailMenu')
  public handleDetailMenu(newValue: boolean) {
    if (newValue) {
      this.$nextTick(() => {
        const filterItemsTool = this.$refs[
          'filter-items-toolbar'
        ] as FilterItems
        filterItemsTool?.initFilterItems()
      })
    }
  }

  // 监听 graphId 变化
  @Watch('graphId')
  public graphIdChange() {
    console.log(this.graphId)
  }

  @Watch('searchInput')
  public searchTextChange(newValue: string) {
    // console.log(this.graphId)
    KGraphStore.setSearchInput(newValue)
  }

  // 监听 graphId 变化
  @Watch('graphVisWidth')
  public graphVisWidthChange() {
    this.changeGraphsize()
  }

  async saveWidget(widget: any) {
    const updateData = JSON.parse(JSON.stringify(widget.data))
    updateData.chartOptions.value = []
    const parameters = {
      data: {
        dashboardId: -1,
        data: updateData,
        name: widget.name,
        randomId: Math.floor(Math.random() * 10000),
        tableName: widget.tid,
        type: widget.type,
        projectId: localStorage.getItem('projectId'),
      },
    }
    const response = await saveWidget(parameters)
    return response.data
  }

  async updateWidget(widget: any) {
    const parameters = {
      data: {
        dashboardId: -1,
        data: widget.data,
        id: this.myModalVisualizationConfig.widgetId,
        name: widget.name,
        type: widget.type,
        projectId: localStorage.getItem('projectId'),
      },
    }
    const response = await updateWidget(parameters)
    return response.data
  }

  public async onMyModalVisualizationSave(widget: any) {
    console.log('onMyModalVisualizationSave:', widget)
    const responseData =
      this.myModalVisualizationConfig.widgetId === undefined
        ? await this.saveWidget(widget)
        : await this.updateWidget(widget)
    const tableName = widget.tid
    const updatedData = this.selectedNodeData
    const { title } = widget.data.chartOptions
    const newItem: any = {
      attrName: title !== undefined && title.length > 0 ? title : '可视化图表',
      tableName,
      visible: true,
      type: 'chart',
      widgetId: responseData.result,
      _id_: this.selectedNodeData.basic.next_id_,
      // widget: widget.data,
    }
    const newChartItem = {
      id: newItem.widgetId,
      chartType: widget.data.chartType,
      imgSrc: this.chartMaps[widget.data.chartType[0]]?.filter(
        (chartConfig: any) => chartConfig.name === widget.data.chartType[1]
      )[0]?.imgSrc,
      data: widget.data,
      tableName,
    }
    if (responseData.code === 100) {
      this.$message.success('保存可视化成功')
      if (this.myModalVisualizationConfig.widgetId === undefined) {
        // save
        this.selectedNodeData.basic.next_id_ += 1
        this.selectedNodeData.layout.next_id_ =
          this.selectedNodeData.basic.next_id_
        updatedData.basic.items[0].children.push(newItem)
        updatedData.layout.items[0].children.push(newItem)
        updatePanelInfoByVid({
          data: {
            id: this.selectedNodeData.id,
            layout: updatedData.layout,
            basic: updatedData.basic,
          },
        }).then((response: any) => {
          if (response.data.code === 100) {
            this.$message.success('更新panel成功')
            // 更新node text
            this.graph.updateItem(this.selectedNodeModel.id, {
              _numChart_: this.selectedNodeModel._numChart_ + 1,
            })
            this.graph.refresh()
            // 更新gallery
            this.chartsGallery[tableName].push(newChartItem)
          }
        })
      } else {
        // edit widget后端会自动覆盖 attrName需手动修改

        const basicIndex = updatedData.basic.items[0].children.findIndex(
          (chartItem: any) => {
            return (
              chartItem.widgetId === this.myModalVisualizationConfig.widgetId
            )
          }
        )
        updatedData.basic.items[0].children[basicIndex].attrName = title
        const layoutIndex = updatedData.layout.items[0].children.findIndex(
          (chartItem: any) => {
            return (
              chartItem.widgetId === this.myModalVisualizationConfig.widgetId
            )
          }
        )
        updatedData.layout.items[0].children[layoutIndex].attrName = title
        await updatePanelInfoByVid({
          data: {
            id: this.selectedNodeData.id,
            layout: updatedData.layout,
            basic: updatedData.basic,
          },
        })
        const galleryIndex = this.chartsGallery[tableName].findIndex(
          (chartItem: any) => {
            return chartItem.id === this.myModalVisualizationConfig.widgetId
          }
        )
        this.chartsGallery[tableName][galleryIndex] = newChartItem
        // 选中全部可视化
        if (this.nodeEditTableIndex === -1) {
          this.getTableData('ALL')
        }
      }

      this.myModalVisualizationConfig = {
        visible: false,
        chartConfig: {},
        datasetList: {
          dataset: [],
          pipeline: [],
        },
        widgetId: undefined,
      }
      this.$forceUpdate()
    } else {
      this.$message.error('保存可视化失败')
    }
  }

  public onSave() {}

  async onDelete(widget: any) {
    const responseData = await this.deleteWidget(widget)
    if (responseData.code === 100) {
      this.$message.success('删除可视化成功')
      // 更新panel
      this.selectedNodeData.basic.items[0].children =
        this.selectedNodeData.basic.items[0].children.filter((item: any) => {
          return !(item.widgetId === widget.id && item.type === 'chart')
        })
      this.selectedNodeData.layout.items[0].children =
        this.selectedNodeData.layout.items[0].children.filter((item: any) => {
          return !(item.widgetId === widget.id && item.type === 'chart')
        })
      updatePanelInfoByVid({
        data: {
          id: this.selectedNodeData.id,
          layout: this.selectedNodeData.layout,
          basic: this.selectedNodeData.basic,
        },
      }).then((response: any) => {
        if (response.data.code === 100) {
          this.$message.success('更新panel成功')
          this.graph.updateItem(this.selectedNodeModel.id, {
            // 更新node text
            _numChart_: this.selectedNodeModel._numChart_ - 1,
          })
          this.graph.refresh()
          this.chartsGallery[widget.tableName] = this.chartsGallery[
            widget.tableName
          ].filter((chartItem: any) => {
            return chartItem.id !== widget.id
          })
          this.widgets = this.widgets.filter((chartItem: any) => {
            return chartItem.id !== widget.id
          })
        }
      })
    } else {
      this.$message.error('删除可视化失败')
    }
  }

  async deleteWidget(widget: any) {
    const response = await deleteWidget({
      data: {
        id: widget.id,
        projectId: localStorage.getItem('projectId'),
        type: 'task',
      },
    })
    return response.data
  }

  editMyVisualization(widget: any, widgetType: 'dataset' | 'pipeline') {
    // console.log('editMyVisualization', widget)
    const datasetList: any = {
      dataset: [],
      pipeline: [],
    }
    const name = this.nodeEditTableList.filter((table: any) => {
      // console.log(table, widget)
      return table.attrValue === widget.tableName
    })[0].attrName
    datasetList[widgetType].push({
      id: widget.tableName,
      name,
      tableName: widget.tableName,
    })
    //  getWidgetData
    const query: any = {
      data: {
        id: widget.data.chartOptions.dataId,
        type: widget.data.chartOptions.dataType,
        widgetJson: widget.data.widgetJson,
      },
    }

    query.data.tableName = query.data.id

    getWidgetData(query)
      .then((response) => {
        if ([100, 1106].includes(response.data.code)) {
          const chartType = Array.isArray(widget.chartType)
            ? widget.chartType[1]
            : widget.chartType

          const result = parseGetDataResult(response.data.result)
          //  双轴图value 略不相同
          if (chartType === 'dualAxesChart') {
            widget.data.chartOptions.value = [result, result]
          } else {
            widget.data.chartOptions.value = result
          }

          this.myModalVisualizationConfig = {
            chartConfig: {
              ...widget.data,
              widgetJson: {
                ...widget.data.widgetJson,
              },
              widgetType,
            },
            datasetList,
            visible: true,
            widgetId: widget.id,
          }
          this.$forceUpdate()
        }
      })
      .catch(() => {
        this.$message.error('获取chart 画图数据失败')
      })
  }

  public openPushRecord() {
    // TODO
  }

  public getTableData(tableName: string) {
    this.tableIsLoading = true
    if (tableName === undefined) {
      // nothing
    } else if (tableName === 'ALL') {
      this.widgets = []
      Object.keys(this.chartsGallery).forEach((tableNameItem: any) => {
        this.widgets = [...this.widgets, ...this.chartsGallery[tableNameItem]]
      })
    } else {
      this.currentPage = 1
      this.widgets = this.chartsGallery[tableName]
      getData({
        data: {
          tableName,
          type: 'dataset',
          tableJson: {
            curPage: this.currentPage,
            pageSize: 13,
            name: '_record_id_',
            filters: [],
            sortCol: [],
          },
        },
      }).then((tableResponse: any) => {
        this.tableIsLoading = false
        if (tableResponse.data.code === 100) {
          const rawTableData = tableResponse.data.result
          this.nodeTableColumns = rawTableData.columns.map((column: any) => {
            return {
              title: column.name,
              dataIndex: column.name,
              key: column.name,
              name: column.name,
            }
          })
          this.nodeTableData = rawTableData.data.map((item: any) => {
            const result: any = {}
            rawTableData.columns.forEach((column: any, index: number) => {
              result[column.name] = item[index]
            })
            return result
          })
          this.totalPages = rawTableData.page ? rawTableData.page.totalPages : 0
        } else {
          this.$message.error(tableResponse.data.message)
        }
      })
    }
  }

  //  初始化resize 事件
  public initNotGisPanelViewEvent() {
    const minWidth = 44
    const resizeBar = document.querySelector('#node-panel-resize-line') as any

    // 鼠标按下事件
    const mouseDownEvent = ($event: any) => {
      const graphBox = document.querySelector('.not-gis-panel') as HTMLElement
      const remainWidth = graphBox.clientWidth - minWidth
      //  图谱可视化区域 最大可调整宽度
      const maxWidth = remainWidth - 100
      // console.log('mousedown')

      const initCurrentWidth = this.nodePanelWidth
      const startX = $event.clientX
      const mouseMoveEvent: any = (event: any) => {
        const endX = event.clientX
        const moveLength = endX - startX
        let newWidth = initCurrentWidth + moveLength
        if (newWidth > maxWidth) {
          newWidth = maxWidth
        } else if (newWidth < minWidth) {
          newWidth = minWidth
        }
        this.nodePanelWidth = newWidth
        if (this.collapseLeftPanel === false && newWidth === 44) {
          this.collapseLeftPanel = true
        } else if (this.collapseLeftPanel === true && newWidth > 44) {
          this.collapseLeftPanel = false
        }
        this.$forceUpdate()
      }

      document.addEventListener('mousemove', mouseMoveEvent)
      const mouseUpEvent: any = () => {
        document.removeEventListener('mousemove', mouseMoveEvent)
        document.removeEventListener('mouseup', mouseUpEvent)
        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
        resizeBar.releaseCapture && resizeBar.releaseCapture()
      }
      document.addEventListener('mouseup', mouseUpEvent)
      // eslint-disable-next-line @typescript-eslint/no-unused-expressions
      resizeBar.setCapture && resizeBar.setCapture()
      return false
    }

    resizeBar.addEventListener('mousedown', mouseDownEvent)
    this.$once('hook:beforeDestroy', () => {
      resizeBar.removeEventListener('mousedown', mouseDownEvent)
    })
  }

  public openAddVisualiztionModal() {
    const datasetList: any = {
      dataset: [],
      pipeline: [],
      datasetSelectEnable: true,
    }
    const tableData = this.nodeEditTableList[this.nodeEditTableIndex]
    datasetList.dataset.push({
      id: tableData.attrValue,
      name: tableData.attrName,
      tableName: tableData.attrValue,
    })
    this.nodeEditTableList.forEach((item: any, index: number) => {
      if (index !== this.nodeEditTableIndex) {
        datasetList.dataset.push({
          id: item.attrValue,
          name: item.attrName,
          tableName: item.attrValue,
        })
      }
    })
    this.myModalVisualizationConfig = {
      chartConfig: {
        widgetType: 'dataset',
        chartOptions: {
          dataId: tableData.attrValue,
          dataType: 'dataset',
        },
      },
      datasetList,
      visible: true,
      widgetId: undefined,
    }
    this.$forceUpdate()
  }

  public onCheckFilterClass(checkedKeys: any) {
    KGraphStore.setClassKeys(checkedKeys)
  }

  public onCheckFilterRelation(checkedKeys: any) {
    KGraphStore.setRelationKeys(checkedKeys)
  }

  public async openLoadDataModel() {
    // const result = await queryByUserId({ data: {} })
    const result = await queryUserDatalist({
      data: { projectId: this.projectId, type: 'kg' },
    })
    this.graphListData = result.data.result
    // this.graphListData = result.data.result.map((item: any) => {
    //   item.key = item.id.toString()
    //   return { title: item.name, id: item.id, key: item.key }
    // })
    this.loadDataModalVisible = true
  }

  public openSearchInput() {
    console.log('openSearchInput:', this.isLoadGraphData)
    if (this.isLoadGraphData) {
      this.searchInputVisible = true
    }
  }

  public openFilterContent() {
    if (this.isLoadGraphData) {
      this.filterContentVisible = !this.filterContentVisible
    }
  }

  public closeFilterContent() {
    this.filterContentVisible = false
  }

  // 预处理后端数据成前端所需格式(需要处理前缀)
  public preprocessData(rawData: any, type?: string) {
    console.log('t', type, rawData)
    const data = JSON.parse(JSON.stringify(rawData))
    data.nodes = data.vertices
    if (data.nodes !== undefined) {
      if (type && type === 'search' && this.searchInit) {
        data.nodes.forEach((item: any) => {
          item.id = item.name.toString()
          item.label = item.name
          // node的subObjectList有值，代表是非叶子节点
          item.collapsible = item.subObjectList?.length > 0
          item.collapse = !item.collapsible
          item.attrs = item.attributes
          item.className = item.concept ? '' : item.className
        })
      } else {
        data.nodes.forEach((item: any) => {
          item.id = item.name.toString()
          item.label = item.name
          // node的subObjectList有值，代表是非叶子节点
          item.collapsible = item.subObjectList?.length > 0
          item.collapse = !item.collapsible
          item.attrs = item.attributes
          item.className = item.concept ? '' : item.className
        })
      }
    }
    if (data.edges !== undefined) {
      data.edges.forEach((item: any) => {
        item.label = item.relation
        item.source = item.source.toString()
        item.target = item.target.toString()
      })
    }
    this.$forceUpdate()
    console.log('preprocessData:', data)
    return data
  }

  public pushData() {
    // TODO - hide modal tmp
    // this.pushDataModalConfig = {
    //   visible: true,
    // }
  }

  public filterContent() {
    // TODO
  }

  /**
   * 表格内容滚动 emit
   */
  private onTableScroll(data: any) {
    const { scrollToBottom } = data // 是否滚动到底部
    if (
      scrollToBottom &&
      this.currentPage < this.totalPages &&
      !this.isNodeTableLoading
    ) {
      // console.log(1)
      this.isNodeTableLoading = true
      this.currentPage += 1
      getData({
        data: {
          tableName: this.nodeEditTableList[this.nodeEditTableIndex].attrValue,
          type: 'dataset',
          tableJson: {
            curPage: this.currentPage,
            pageSize: 13,
            name: '_record_id_',
            filters: [],
            sortCol: [],
          },
        },
      }).then((tableResponse: any) => {
        if (tableResponse.data.code === 100) {
          const rawTableData = tableResponse.data.result
          this.nodeTableData = this.nodeTableData.concat(
            rawTableData.data.map((item: any) => {
              const result: any = {}
              rawTableData.columns.forEach((column: any, index: number) => {
                result[column.name] = item[index]
              })
              return result
            })
          )
          this.totalPages = rawTableData.page ? rawTableData.page.totalPages : 0
        }
      })
    }
  }

  public async onSearchInput() {
    this.isGraphVisLoading = true
    if (this.searchInput.length > 0) {
      this.searchInit = true
    } else {
      this.searchInit = false
    }
    const formData = new FormData()
    formData.append('knowledgeGraphFileId', this.graphId.toString())
    formData.append('searchInput', this.searchInput)
    const response = await searchKnowledgeGraph({
      data: formData,
    })
    const { code, data } = response.data
    if (code === 200) {
      const shapeData = shapePrefixInfo(data, this.baseUri)
      KGraphStore.setUpdateGisMapTime()
      //  搜索之后重置checked items
      this.initFilterCheckedItem({
        checkedFilterItems: shapeData.checkedFilterItems,
      })
      this.graphData = this.preprocessData(shapeData, 'search')
      KGraphStore.setFilterRawData(this.graphData)
      this.rightClickDict = {}
      this.rerenderGraph()
    }
  }

  public async onFilterItems() {
    this.isGraphVisLoading = true
    const formData = new FormData()
    formData.append('projectId', this.projectId.toString())
    const withClass = JSON.stringify(
      this.checkedClassKeys.map((item: any) => {
        return `${KGraphStore.baseUri}#${item}`
      })
    )
    const hasRelation = JSON.stringify(
      this.checkedRelationKeys.map((item: any) => {
        return `${KGraphStore.baseUri}#${item}`
      })
    )
    formData.append('withClass', withClass)
    formData.append('hasRelation', hasRelation)
    formData.append('searchInput', this.searchInput)
    const response = await queryKnowledgeGraph({
      data: formData,
      // {
      //   projectId: this.projectId,
      //   id: this.graphId,
      //   withClass: this.checkedClassKeys,
      //   hasRelation: this.checkedRelationKeys,
      //   searchInput: this.searchInput,
      // },
    })
    const { code, data } = response.data
    if (code === 200) {
      //  update dashboard info
      KGraphStore.setUpdateGisMapTime()
      this.closeFilterContent()
      if (this.checkedRelationKeys.length === 0) {
        data.edges = []
      }
      const shapeData = shapePrefixInfo(data, this.baseUri)
      //  在每次filter 的时候 重置 rawData
      KGraphStore.setFilterRawData(this.preprocessData(shapeData))
      this.graphData = this.preprocessData(shapeData)
      this.rightClickDict = {}
      this.rerenderGraph()
    }
  }

  public confirmDelete = () => {
    Modal.confirm({
      title: '确定要删除图谱吗？',
      content:
        '图谱删除后，系统构建中关联的图表也将会被删除，请确认是否执行此操作！',
      okText: '确定',
      cancelText: '取消',
      onOk: this.deleteGraph,
    })
  }

  public async deleteGraph() {
    //  清空打开的实体面板
    KGraphStore.setEntityPanelList([])
    const formData = new FormData()
    formData.append('projectId', this.projectId.toString())
    const response = await unbindProject({
      data: formData,
    })
    if (response.data.code === 200) {
      // this.graphId = -1
      KGraphStore.setGraphId(-1)
      KGraphStore.setFilterRawData({})
      // this.rawData = {}
      this.graphData = {}
      this.selectedRows = []
      this.graphListData = []
      this.isLoadGraphData = false
      this.selectedGraphName = ''
      this.searchInput = ''
      this.searchInputVisible = false
      this.rightClickDict = {}
      KGraphStore.setClassKeys([])
      KGraphStore.setRelationKeys([])
      this.filterContentVisible = false
      this.nodeEditPanelVisible = false
      this.graph.destroyLayout()
      this.graph.clear()
      this.graph.destroy()
      KGraphStore.setClearDashboardTime()
      //  clear filter checked options
      this.withInitFilter = false
      KGraphStore.setClassKeys([])
      KGraphStore.setRelationKeys([])
      this.selectedLayout = KGraphMaps.DAGRE.alias
      KGraphStore.setBaseUri('')
    } else {
      this.$message.error('删除图谱数据出错')
    }
  }

  public dragMode() {
    if (this.isLoadGraphData) {
      this.selectedMode = 'default'
      this.graph.setMode('default')
    }
  }

  public brushMode() {
    this.$message.info('暂不需要brush')
    // if (this.isLoadGraphData) {
    //   this.selectedMode = 'brush'
    //   this.graph.setMode('brush')
    // }
  }

  public resetLayout() {
    if (this.isVisitor || this.isLock || !this.graph) {
      return
    }
    this.isGraphVisLoading = true
    this.graph.destroyLayout()
    switch (this.selectedLayout) {
      case KGraphMaps.GRID.alias:
        this.graph.updateLayout(KGraphMaps.GRID.layout)
        break
      case KGraphMaps.DAGRE.alias:
        this.graph.updateLayout(KGraphMaps.DAGRE.layout)
        break
      case KGraphMaps.FRUCHTERMAN.alias:
        this.graph.updateLayout(KGraphMaps.FRUCHTERMAN.layout)
        break
      case KGraphMaps.RADIAL.alias:
        this.graph.updateLayout(KGraphMaps.RADIAL.layout)
        break
      default:
        break
    }
  }
  /**
   * 搜索之后需要重新渲染
   */
  public rerenderGraph(layout?: any) {
    switch (this.selectedLayout) {
      case KGraphMaps.GRID.alias:
        this.gridLayout(layout)
        break
      case KGraphMaps.DAGRE.alias:
        this.dagreLayout(layout)
        break
      case KGraphMaps.FRUCHTERMAN.alias:
        this.fruchtermanLayout(layout)
        break
      case KGraphMaps.RADIAL.alias:
        this.radialLayout(layout)
        break
      default:
        break
    }
  }
  /**
   * grid布局
   */
  public gridLayout(layout?: any) {
    this.selectedLayout = KGraphMaps.GRID.alias
    this.initRightClickDict()
    const graphData = this.shapeGraphData()
    this.graph.destroyLayout()
    this.graph.updateLayout({
      ...KGraphMaps.GRID.layout,
      ...layout,
    })
    this.graph.data(graphData)
    this.graph.render()
  }
  /**
   * dagre布局
   */
  public dagreLayout(layout?: any) {
    this.selectedLayout = KGraphMaps.DAGRE.alias
    this.initRightClickDict()
    const graphData = this.shapeGraphData()
    this.graph.destroyLayout()
    this.graph.updateLayout({
      ...KGraphMaps.DAGRE.layout,
      ...layout,
    })
    this.graph.data(graphData)
    this.graph.render()
  }
  /**
   * fruchterman布局
   */
  public fruchtermanLayout(layout?: any) {
    this.graph.destroyLayout()
    this.isGraphVisLoading = true
    this.selectedLayout = KGraphMaps.FRUCHTERMAN.alias
    this.initRightClickDict()
    const graphData = this.shapeGraphData()
    this.graph.updateLayout({
      ...KGraphMaps.FRUCHTERMAN.layout,
      ...layout,
    })
    this.graph.data(graphData)
    this.graph.render()
  }
  /**
   * radial布局
   */
  public radialLayout(layout?: any) {
    this.graph.destroyLayout()
    this.isGraphVisLoading = true
    this.selectedLayout = KGraphMaps.RADIAL.alias
    this.initRightClickDict()
    const graphData = this.shapeGraphData()
    this.graph.destroyLayout()
    this.graph.updateLayout({
      ...KGraphMaps.RADIAL.layout,
      ...layout,
      strictRadial: graphData?.edges?.length !== 0,
      sortBy: !this.searchInit ? 'isCluster' : undefined,
      sortStrength: this.searchInit ? 10 : 5000,
      unitRadius: this.searchInit ? 80 : 120,
    })
    this.graph.data(graphData)
    this.graph.render()
  }
  /**
   * 初始化布局
   * @param layout
   */
  public initLayout(layout?: any) {
    let LayoutConstructor: any
    let initParameters: any
    switch (this.selectedLayout) {
      case KGraphMaps.GRID.alias:
        LayoutConstructor = G6.Layout.grid
        initParameters = {
          ...KGraphMaps.GRID.layout,
          ...layout,
        }
        break
      case KGraphMaps.DAGRE.alias:
        LayoutConstructor = G6.Layout.dagre
        initParameters = {
          ...KGraphMaps.DAGRE.layout,
          ...LayoutConstructor,
        }
        break
      case KGraphMaps.FRUCHTERMAN.alias:
        LayoutConstructor = G6.Layout.fruchterman
        initParameters = {
          ...KGraphMaps.FRUCHTERMAN.layout,
          ...layout,
        }
        break
      case KGraphMaps.RADIAL.alias:
        LayoutConstructor = G6.Layout.radial
        initParameters = {
          ...KGraphMaps.RADIAL.layout,
          ...layout,
        }
        break
      default:
        break
    }
    return {
      LayoutConstructor,
      initParameters,
    }
  }

  /**
   * 根据不同布局对数据进行整形
   */
  public shapeGraphData(): any {
    const graphData = cloneDeep({
      nodes: this.graphData.vertices, // 取vertices，nodes中有上次布局的位置信息，对接下来的布局有影响
      edges: this.graphData.edges,
    })
    graphData?.nodes?.forEach((node: any) => {
      // node的subObjectList有值，代表是非叶子节点
      node.collapsible = node.subObjectList?.length > 0
      node.collapse = node.collapsible
      const shapeNode = this.shapeNodeData(node)
      node = shapeNode
    })
    graphData?.edges?.forEach((edge: any) => {
      const shapeEdge = this.shapeEdgeData(edge)
      edge = shapeEdge
    })
    console.log('after shapeGraphData:', graphData)
    return {
      nodes: graphData.nodes,
      edges: graphData.edges,
    }
  }

  /**
   * 根据不同布局对节点数据进行整形
   */
  public shapeNodeData(node: any): any {
    if (
      this.selectedLayout === KGraphMaps.DAGRE.alias ||
      this.selectedLayout === KGraphMaps.GRID.alias
    ) {
      node.type = 'node-card'
    } else {
      node.type = 'circle'
      node.anchorPoints = [
        [0.5, 0],
        [1, 0.5],
        [0.5, 1],
        [0, 0.5],
      ]
    }
    const clusters = cloneDeep(this.rawData?.filterItems?.class)
    // fruchterman/radial布局需要对数据进行处理
    // 知识的聚类中心是概念，实体的聚类中心是知识，知识是聚类的子集
    if (
      this.selectedLayout === KGraphMaps.FRUCHTERMAN.alias ||
      this.selectedLayout === KGraphMaps.RADIAL.alias
    ) {
      // node.size = this.selectedLayout === KGraphMaps.FRUCHTERMAN.alias ? 16 : 20
      node.size = 20
      const { cluster, colorCluster, color } = this.classify(node, clusters)
      node.cluster = cluster
      node.colorCluster = colorCluster
      node.style = node.collapsible
        ? {
            fill: color,
            stroke: color,
          }
        : {
            fill: 'transparent',
            stroke: color,
          }
      node.labelCfg = {
        position: 'center',
        style: {
          textAlign: 'center',
          textBaseline: 'middle',
          y: 0,
          x: 0,
          lineHeight: 25,
          fontSize: 6,
          text: fittingString(node.label, 120, 12),
          fill: '#373B52',
          opacity: 1,
        },
      }
    }
    const { cluster, colorCluster, color, strokeColor } = this.classify(
      node,
      clusters
    )
    node.cluster = cluster
    node.colorCluster = colorCluster
    node.style = node.collapsible
      ? {
          fill: color,
          stroke: color,
        }
      : {
          fill: 'transparent',
          stroke: color,
        }
    node.isCluster = `${node.colorCluster}`
    node.stateStyles = {
      selected: {
        lineWidth:
          this.selectedLayout === KGraphMaps.FRUCHTERMAN.alias ||
          this.selectedLayout === KGraphMaps.RADIAL.alias
            ? 1
            : 2,
        fill: node.collapsible ? color : 'transparent',
        stroke: strokeColor,
        shadowColor: strokeColor,
        shadowOffsetX: 1,
        shadowOffsetY: 1,
        shadowBlur: ShadowBlur.HIGH,
        'text-shape': {
          fontWeight: 600,
        },
      },
    }
    return {
      ...node,
    }
  }
  /**
   * 根据不同布局对边数据进行整形
   */
  public shapeEdgeData(edge: any): any {
    if (
      this.selectedLayout === KGraphMaps.FRUCHTERMAN.alias ||
      this.selectedLayout === KGraphMaps.RADIAL.alias
    ) {
      edge.style = {
        ...edge?.style,
        shadowColor: 'transparent',
        shadowBlur: ShadowBlur.INIT,
        endArrow: {
          ...edge?.style?.endArrow,
          path: 'M 0,0 L 4,2 L 4,-2 Z',
        },
      }
      edge.stateStyles = {
        hover: {
          'text-shape': {
            opacity: 1,
          },
        },
        selected: {
          stroke: '#979797',
          lineWidth: 2,
          shadowColor: 'transparent',
          // shadowColor: '#979797',
          // shadowBlur: ShadowBlur.HIGH,
          endArrow: {
            path: 'M 0,0 L 8,4 L 8,-4 Z',
            fill: '#979797',
          },
        },
      }
    }
    return edge
  }
  /**
   *
   */
  public getColorSet(clusters: any): any {
    // 根节点
    const root = 'Thing'
    const keys = Object.keys(clusters)
    const colorSet: any = {}
    // 根节点用一种颜色，下面的一级节点分别随机分配颜色
    colorSet[root] = colorRoot
    const setColor = (key: any) => {
      if (clusters[key]) {
        clusters[key].forEach((cKey: any) => {
          colorSet[cKey] = colorSet[key]
          if (clusters[cKey]) {
            setColor(cKey)
          }
        })
      }
    }
    clusters[root].forEach((key: any, index: number) => {
      colorSet[key] = colors[index % 12]
      setColor(key)
    })

    keys.forEach((key: any) => {
      if (key !== root) {
        clusters[key].forEach((cKey: any) => {
          colorSet[cKey] = colorSet[key]
        })
      }
    })
    return colorSet
  }
  /**
   * 获取节点分类
   */
  public classify(node: any, clusters: any): any {
    const keys = Object.keys(clusters)
    // 如果是概念节点
    if (node.concept) {
      const cluster = keys.find((key: string) => {
        return clusters[key].includes(node.label)
      })
      // cluster是布局用来聚类的参数
      // colorCluster是大类所对应的颜色
      node.cluster = cluster
      if (keys.includes(node.label)) {
        node.colorCluster = node.label
      } else {
        node.colorCluster = cluster
      }
      node.color = this.colorSet[node.label]
        ? this.colorSet[node.label].concept
        : colorRoot.concept
      node.strokeColor = this.colorSet[node.label]
        ? this.colorSet[node.label].conceptStroke
        : colorRoot.concept
    } else {
      const cluster = keys.find((key: string) => {
        return clusters[key].includes(node.className)
      })
      node.cluster = node.className
      node.colorCluster = cluster
      node.color = this.colorSet[node.className]
        ? this.colorSet[node.className].entity
        : colorRoot.entity
      node.strokeColor = this.colorSet[node.className]
        ? this.colorSet[node.className].entityStroke
        : colorRoot.entity
    }
    return {
      cluster: node.cluster,
      colorCluster: node.colorCluster,
      color: node.color,
      strokeColor: node.strokeColor,
    }
  }

  public async onLoadDataModelOk(value: any) {
    this.confirmLoading = true
    KGraphStore.setGraphId(Number(value))
    // KGraphStore.setGraphId(Number(selectedRows[0].id))
    // this.graphId = Number(selectedRows[0].id)
    // 加载图谱到项目
    const formData = new FormData()
    formData.append('knowledgeGraphFileId', this.graphId.toString())
    formData.append('projectId', this.$route.params.id)
    const response = await bindProject({
      data: formData,
    })
    const { code } = response.data
    if (code === 200) {
      this.isLoadGraphData = true
      this.selectedGraphName =
        this.graphListData[0]?.dataset?.find((element: any) => {
          return element.id === +this.graphId
        })?.name || ''
      // this.selectedGraphName = selectedRows[0].name
      this.renderGraph()
    }
    this.confirmLoading = false
    this.resetLoadDataModel()
  }

  public onLoadDataModelCancel() {
    this.resetLoadDataModel()
  }

  private resetLoadDataModel() {
    this.loadDataModalVisible = false
  }

  // 渲染可视化
  public async renderGraph() {
    this.isGraphVisLoading = true
    const formData = new FormData()
    formData.append('projectId', this.projectId.toString())
    const withClass = this.withInitFilter
      ? JSON.stringify(
          this.checkedClassKeys.map((item: any) => {
            return `${KGraphStore.baseUri}#${item}`
          })
        )
      : 'allClasses'
    const hasRelation = this.withInitFilter
      ? JSON.stringify(
          this.checkedRelationKeys.map((item: any) => {
            return `${KGraphStore.baseUri}#${item}`
          })
        )
      : 'allRelations'
    formData.append('withClass', withClass)
    formData.append('hasRelation', hasRelation)
    await queryKnowledgeGraph({
      data: formData,
      // {
      //   projectId: this.projectId,
      //   id: this.graphId,
      //   withClass: this.withInitFilter ? this.checkedClassKeys : undefined,
      //   hasRelation: this.withInitFilter ? this.checkedRelationKeys : undefined,
      // },
    }).then((response: any) => {
      const { data } = response.data
      // 用户第一次进来，也需要query接口返回baseUri
      KGraphStore.setBaseUri(data.baseUri)
      KGraphStore.setUpdateDashboardTime()
      const shapeData = shapePrefixInfo(data, this.baseUri)
      if (!this.sourceData) {
        KGraphStore.setSourceData(this.preprocessData(shapeData))
      }
      this.initFilterCheckedItem({
        checkedFilterItems: shapeData.checkedFilterItems,
      })

      KGraphStore.setFilterRawData(this.preprocessData(shapeData))
      // this.rawData = this.preprocessData(response.data.result)
      this.rightClickDict = {}
      this.graphData = JSON.parse(JSON.stringify(this.rawData))
      // 初始化颜色分类
      const clusters = cloneDeep(this.rawData?.filterItems?.class)
      this.colorSet = this.getColorSet(clusters)
      console.log(this.graphData, 'this.graphData', this.rawData)
      if (!this.graph || this.graph.destroyed) {
        this.initGraph(this.graphData.filterItems.class)
      }
      const shapeGraphData = this.shapeGraphData()
      this.graph.data(shapeGraphData)
      this.graph.render()
      // 切换到fruchterman/radial布局之后，删除数据再重新加载，需要重新初始化布局，否则布局信息（比如节点、位置）会影响到此次展示
      this.resetLayout()
      this.$nextTick(() => {
        const filterItems = this.$refs['filter-items'] as FilterItems
        filterItems?.initFilterItems()
      })
    })
  }

  public handleSearch = (selectedKeys: any, confirm: any, dataIndex: any) => {
    confirm()[this.searchText] = selectedKeys
    this.searchedColumn = dataIndex
  }

  public handleReset = (clearFilters: any) => {
    clearFilters({ confirm: true })
    this.searchText = ''
  }

  public zoomToTargetNode(id: any, offsetY?: any) {
    const toRatio =
      this.selectedLayout === KGraphMaps.GRID.alias ||
      this.selectedLayout === KGraphMaps.DAGRE.alias
        ? 1
        : 2
    this.graph.zoomTo(toRatio)
    this.graph.focusItem(id)
    const container: any = document.querySelector('#graph-vis')
    offsetY = offsetY || container.clientHeight / 4
    this.graph.translate(0, -offsetY)
  }

  /** 右键菜单值初始化 */
  public initRightClickDict(id?: string) {
    const keys = Object.keys(this.rightClickDict)
    if (id) {
      keys?.forEach((key: any) => {
        if (key !== id) {
          const clickKeys = Object.keys(this.rightClickDict[key]?.rightClick)
          clickKeys?.forEach((clickKey: any) => {
            this.rightClickDict[key].rightClick[clickKey] = false
          })
        }
      })
    } else {
      keys?.forEach((key: any) => {
        const clickKeys = Object.keys(this.rightClickDict[key]?.rightClick)
        clickKeys?.forEach((clickKey: any) => {
          this.rightClickDict[key].rightClick[clickKey] = false
        })
      })
    }
  }

  public initGraph(clusters: any) {
    const self = this
    // 自定义节点
    G6.registerNode(
      'node-card',
      {
        drawShape(cfg: any, group: any) {
          const r = 2
          const { color } = self.classify(cfg, clusters)
          const shape = group.addShape('rect', {
            attrs: {
              x: 0,
              y: 20,
              width: 180,
              height: 45,
              stroke: color,
              lineWidth: 2,
              fill: cfg.collapsible ? color : 'white',
              radius: r,
            },
            name: 'main-box',
            draggable: true,
          })

          // name text
          group.addShape('text', {
            attrs: {
              textAlign: 'center',
              textBaseline: 'middle',
              y: 40,
              x: 95,
              lineHeight: 25,
              fontSize: 16,
              text: fittingString(cfg.label, 120, 12),
              fill: 'rgba(0,0,0, 1)',
            },
            name: 'label',
            draggable: true,
          })
          // if (cfg.collapsible) {
          //   group.addShape('marker', {
          //     attrs: {
          //       x: 8,
          //       y: 40,
          //       r: 6,
          //       cursor: 'pointer',
          //       symbol: cfg.collapse ? G6.Marker.expand : G6.Marker.collapse,
          //       stroke: '#666',
          //       lineWidth: 1,
          //     },
          //     name: 'collapse-icon',
          //   })
          // }

          if (cfg._numTable_ >= 1) {
            group.addShape('image', {
              attrs: {
                x: 5,
                y: 0,
                width: 16,
                height: 16,
                img: require('@/assets/num_tables.svg'),
              },
              name: 'table-number-icon',
            })

            group.addShape('text', {
              attrs: {
                textBaseline: 'middle',
                x: 25,
                y: 8,
                lineHeight: 16,
                text: cfg._numTable_,
                fill: '#000',
              },
              name: 'table-number-text',
            })

            group.addShape('image', {
              attrs: {
                x: 35,
                y: 0,
                width: 16,
                height: 16,
                img: require('@/assets/num_charts.svg'),
              },
              name: 'chart-number-icon',
            })

            group.addShape('text', {
              attrs: {
                textBaseline: 'middle',
                x: 55,
                y: 8,
                lineHeight: 16,
                text: cfg._numChart_,
                fill: 'rgba(0,0,0, 1)',
              },
              name: 'chart-number-text',
            })
          }

          if (cfg.isClass === -1) {
            group.addShape('image', {
              attrs: {
                x: 5,
                y: 32,
                width: 16,
                height: 16,
                img: require('@/assets/gis-icon.svg'),
              },
              name: 'gis-icon',
            })
          }
          return shape
        },
        // update(cfg: any, node: any) {
        //   console.log('registerNode-update:',cfg.collapse,node)
        //   node.get('group').get('children')[2].attrs.symbol = cfg.collapse
        //     ? G6.Marker.expand
        //     : G6.Marker.collapse
        //   if (node.get('group').get('children').length > 6) {
        //     node.get('group').get('children')[6].attrs.text = cfg._numChart_
        //   }
        // },
        update: undefined,
      },
      'single-node'
    )

    const tooltip = new G6.Tooltip({
      offsetX: 10,
      offsetY: 10,
      // the types of items that allow the tooltip show up
      // 允许出现 tooltip 的 item 类型
      itemTypes: ['edge'],
      // custom the tooltip's content
      // 自定义 tooltip 内容
      getContent: (event: any) => {
        const outDiv = document.createElement('div')
        const currentModel = event.item.getModel()
        outDiv.style.width = 'fit-content'
        outDiv.innerHTML = `
      <div>${currentModel.label || currentModel.id}
      </div>`
        // Object.keys(currentModel.attrs).forEach((currentKey: any) => {
        //   outDiv.innerHTML += `<div>${currentKey}: ${currentModel.attrs[currentKey]}</div>`
        // })
        return outDiv
      },
    })

    // 知识图谱：添加节点、边的操作
    const addAction = (item: any, verticesToAdd: any, edgesToAdd: any) => {
      let needRender = false
      const nodeInstance = this.graph.findById(item.id)
      const expandNodes: any = []
      const expandEdges: any = []
      this.sourceData.nodes.forEach((node: any) => {
        if (verticesToAdd.includes(node.id)) {
          // if (this.searchInit) {
          //   node.collapse = true
          // }
          // if (node.collapsible) {
          //   node.collapse = true
          // }
          // 需要位置信息，所以从graphData中获取节点信息
          const graphNode = this.graphDataAfterLayout.nodes.find(
            (graphnode: any) => {
              return graphnode.id === node.id
            }
          )
          let addNodeAfterShape
          // 如果是搜索/筛选操作之后，画布先布局，再选择某个节点进行点击展开操作
          // 此时graphDataAfterLayout并不包含其子节点，只能直接添加node
          if (!graphNode) {
            const addNode = {
              ...node,
            }
            addNodeAfterShape = this.shapeNodeData(addNode)
            this.graph.addItem('node', addNodeAfterShape)
            needRender = true
          } else {
            const findNode = this.graph.findById(node.id)
            addNodeAfterShape = this.shapeNodeData({
              ...graphNode,
              collapse: graphNode.collapsible,
            })
            if (!findNode) {
              this.graph.addItem('node', addNodeAfterShape)
            }
          }
          expandNodes.push(addNodeAfterShape)
        }
      })
      // 还存在一种情况，就是后端返回的边在this.rawData.edges中找不到,但是在this.sourceData中可以找到
      this.sourceData.edges.forEach((edge: any) => {
        if (edgesToAdd.includes(edge.id)) {
          const { source, target } = edge
          const isEdgeExist = this.graph.findById(edge.id)
          const isSourceNodeExist = this.graph.findById(`${source}`)
          const isTargetNodeExist = this.graph.findById(`${target}`)
          // 边在画布上不存在 && 边的起始节点 && 边的目标节点
          if (!isEdgeExist && isSourceNodeExist && isTargetNodeExist) {
            const addEdgeAfterShape = this.shapeEdgeData({
              ...edge,
              label: edge.relation,
              source: `${source}`,
              target: `${target}`,
            })
            this.graph.addItem('edge', addEdgeAfterShape)
          }
          expandEdges.push(edge)
        }
      })
      if (needRender) {
        const saveDataFromGraph = this.graph.save()
        // 以当前视口中心的坐标作为中间，减少操作时图谱的移动闪现
        const { x, y } = this.graph.getViewPortCenterPoint()
        // 不同布局对应的布局参数
        let layout = {}
        if (this.selectedLayout === KGraphMaps.DAGRE.alias) {
          layout = {
            // 用于保证重新布局时的连续性
            preset: saveDataFromGraph.nodes,
          }
        } else if (this.selectedLayout === KGraphMaps.GRID.alias) {
          layout = {
            cols: 9,
          }
        } else if (this.selectedLayout === KGraphMaps.FRUCHTERMAN.alias) {
          layout = {
            center: [x, y],
          }
        } else if (this.selectedLayout === KGraphMaps.RADIAL.alias) {
          layout = {
            center: [x, y],
            unitRadius: 80, // 可选
            focusNode: item.id,
          }
        }
        // fruchterman布局的增量布局效果不好，暂时需要重新渲染全部数据
        if (this.selectedLayout !== KGraphMaps.FRUCHTERMAN.alias) {
          const { LayoutConstructor, initParameters } = this.initLayout(layout)
          const SubLayout = new LayoutConstructor(initParameters)
          SubLayout.init({
            nodes: saveDataFromGraph.nodes,
            edges: saveDataFromGraph.edges,
          })
          const newLayoutData = SubLayout.execute()
          this.graph.positionsAnimate()
          this.graph.data({
            nodes: newLayoutData.nodes,
            edges: newLayoutData.edges,
          })
          this.graph.refresh()
          // 新布局数据需要更新到graphDataAfterLayout
          this.graphDataAfterLayout = {
            ...newLayoutData,
          }
        } else {
          // fruchterman布局时，重新渲染时固定正在展开节点的坐标
          saveDataFromGraph?.nodes.forEach((node: any) => {
            if (node.id === item.id) {
              node.fx = node.x
              node.fy = node.y
            }
          })
          this.graph.changeData(saveDataFromGraph)
        }
      }
      // 高亮展开节点的子节点
      expandNodes.forEach((node: any) => {
        const { id } = node
        const expandNodeInstance = this.graph.findById(id)
        expandNodeInstance.setState('selected', true)
      })
      expandEdges.forEach((edge: any) => {
        const { id } = edge
        const expandEdgeInstance = this.graph.findById(id)
        expandEdgeInstance.setState('selected', true)
      })
      // 当前操作的节点进行高亮展示
      nodeInstance.setState('selected', true)
    }

    // 知识图谱：移除节点、边的操作
    const removeAction = (item: any, verticesToDrop: any, edgesToDrop: any) => {
      const nodeInstance = this.graph.findById(item.id)
      // 当前操作的节点进行高亮展示
      nodeInstance.setState('selected', true)
      // 需要移除的节点
      const removeNodes = this.sourceData.nodes.filter((node: any) => {
        // 前端nodeId是string 后端nodeId是number
        return verticesToDrop.includes(node.id)
      })
      removeNodes.forEach((node: any) => {
        const findNode = this.graph.findById(node.id)
        this.graph.remove(findNode)
      })
      // 需要移除的边
      const removeEdges = this.rawData.edges.filter((edge: any) => {
        return edgesToDrop.includes(edge.id)
      })
      removeEdges.forEach((edge: any) => {
        const findEdge = this.graph.findById(edge.id)
        this.graph.remove(findEdge)
      })
    }

    let contextMenuResult: any

    const contextMenu = new G6.Menu({
      getContent() {
        return contextMenuResult
      },
      handleMenuClick: (target: any, item: any) => {
        const clickTargetValue = target.getAttribute('value')
        const clickNodeId = item.getID()
        const lastestGraphData = this.graph.save()
        // 点击这个节点，把其他节点的右键菜单都初始化一下
        this.initRightClickDict(clickNodeId)
        self.handleHighLight(lastestGraphData, clickNodeId)
        console.log(
          'handleMenuClick',
          this.rightClickDict[clickNodeId],
          clickTargetValue,
          item.getModel()
        )
        // 当前选中菜单需要操作的节点、边集合
        const targets =
          this.rightClickDict[clickNodeId].idsByELabel[clickTargetValue]
        if (
          this.rightClickDict[clickNodeId].rightClick[clickTargetValue] === true
        ) {
          // 取消选中
          this.rightClickDict[clickNodeId].rightClick[clickTargetValue] = false
          let AllCheckedMenuData: any = {
            edges: [],
            objects: [],
          }
          // 需要计算当前菜单有多少已经被选中
          const checkedRightClick = Object.keys(
            this.rightClickDict[clickNodeId].rightClick
          ).filter((click: any) => {
            return this.rightClickDict[clickNodeId].rightClick[click]
          })
          checkedRightClick.forEach((click: any) => {
            const dataSets = this.rightClickDict[clickNodeId].idsByELabel[click]
            if (dataSets) {
              AllCheckedMenuData = {
                edges: (AllCheckedMenuData.edges || []).concat(dataSets.edges),
                objects: (AllCheckedMenuData.objects || []).concat(
                  dataSets.objects
                ),
              }
            }
          })
          // 当前正在操作的菜单：取消高亮
          // 其他选中的菜单保持高亮
          AllCheckedMenuData.objects.forEach((node: any) => {
            const nodeInstance = this.graph.findById(node)
            nodeInstance.setState('selected', true)
          })
          AllCheckedMenuData.edges.forEach((edge: any) => {
            const edgeInstance = this.graph.findById(edge)
            edgeInstance.setState('selected', true)
          })
        } else {
          // 选中 添加节点和边
          this.rightClickDict[clickNodeId].rightClick[clickTargetValue] = true
          let AllCheckedMenuData = targets
            ? cloneDeep(targets)
            : {
                edges: [],
                objects: [],
              }
          // 需要计算当前菜单有多少已经被选中
          const checkedRightClick = Object.keys(
            this.rightClickDict[clickNodeId].rightClick
          ).filter((click: any) => {
            return this.rightClickDict[clickNodeId].rightClick[click]
          })
          checkedRightClick.forEach((click: any) => {
            const dataSets = this.rightClickDict[clickNodeId].idsByELabel[click]
            if (dataSets) {
              AllCheckedMenuData = {
                edges: (AllCheckedMenuData.edges || []).concat(dataSets.edges),
                objects: (AllCheckedMenuData.objects || []).concat(
                  dataSets.objects
                ),
              }
            }
          })
          // 如果页面上有，则高亮，如果没有，则添加并高亮
          addAction(
            item.getModel(),
            AllCheckedMenuData?.objects || [],
            AllCheckedMenuData?.edges || []
          )
        }
        // 如果改变后rightClickMenu的值都为true或false，相应的改变collapse
        const values = Object.values(
          this.rightClickDict[clickNodeId].rightClick
        )
        if (values.length > 0) {
          if (
            values.every((value: any) => {
              return value === true
            })
          ) {
            this.graph.updateItem(clickNodeId, {
              collapse: false,
            })
          }
          // else if (
          //   values.every((value: any) => {
          //     return value === false
          //   })
          // ) {
          //   this.graph.updateItem(clickNodeId, {
          //     collapse: true,
          //   })
          // }
        }
      },
      // offsetX and offsetY include the padding of the parent container
      // 需要加上父级容器的 padding-left 16 与自身偏移量 10
      offsetX: 16 + 10,
      // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
      offsetY: 0,
      // the types of items that allow the menu show up
      // 在哪些类型的元素上响应
      itemTypes: ['node'],
    })

    const container: any = document.querySelector('#graph-vis')
    const width = container.scrollWidth
    const height = container.scrollHeight || 1000
    this.width = container.clientWidth
    this.height = container.clientHeight

    this.graph = new G6.Graph({
      container,
      width,
      height,
      layout: this.selectedLayout,
      plugins: [tooltip, contextMenu],
      defaultNode: {
        type: 'node-card',
        labelCfg: {
          style: {
            opacity: 0,
          },
        },
      },
      defaultEdge: {
        type: 'line',
        size: 1,
        color: '#97979780',
        style: {
          endArrow: {
            path: 'M 0,0 L 8,4 L 8,-4 Z',
            fill: '#97979780',
          },
          radius: 20,
          lineAppendWidth: 3,
        },
        labelCfg: {
          style: {
            opacity: 0,
          },
        },
      },
      nodeStateStyles: {
        selected: {
          lineWidth: 2,
          // stroke: '#ff3300',
          label: {
            // fill: '#ff3300',
            // opacity: OPACITY.DARK.fillOpacity,
          },
          // 'text-shape': {
          //   // fill: '#ff3300',
          // }
        },
      },
      edgeStateStyles: {
        hover: {
          'text-shape': {
            opacity: 1,
          },
        },
        selected: {
          stroke: '#979797',
          lineWidth: 2,
          shadowColor: 'transparent',
          endArrow: {
            path: 'M 0,0 L 8,4 L 8,-4 Z',
            fill: '#979797',
          },
          // shadowColor: '#979797',
          // shadowBlur: ShadowBlur.HIGH,
        },
      },
      modes: {
        default: [
          'drag-canvas',
          'zoom-canvas',
          { type: 'click-select', multiple: false },
          'drag-node',
        ], // 默认
        brush: [
          // 框选
          'click-select',
          'drag-node',
          {
            type: 'brush-select',
            trigger: 'drag',
            includeEdges: false,
          },
        ],
      },
      fitView: true,
      // animate: true,
    })

    // const zoomToTargetNode = (id: any, offsetY: any) => {
    //   const cilckNodeModel = this.graph.findById(id).getModel()
    //   this.graph.zoomTo(1)
    //   // console.log(cilckNodeModel.x, this.graph.getWidth())
    //   this.graph.moveTo(
    //     -(cilckNodeModel.x - this.graph.getWidth() / 2 + 190),
    //     -(cilckNodeModel.y - this.graph.getHeight() / 2 + offsetY)
    //   )
    // }

    // 展开和收起节点
    const changeCollapse = async (nodeData: any) => {
      console.log('changeCollapse-nodeData:', nodeData, nodeData.collapse)
      if (nodeData.collapse === true) {
        // 展开节点
        const formData = new FormData()
        formData.append('knowledgeGraphFileId', this.graphId.toString())
        formData.append(
          'vertxName',
          window.btoa(encodeURIComponent(`${this.baseUri}#${nodeData.id}`))
        )
        const response = await expandVertice({
          data: formData,
        })
        const { data } = response.data
        const shapeData = shapePrefixInfo(data, this.baseUri)
        addAction(nodeData, shapeData.vertexList, shapeData.edgesList)
      } else if (nodeData.collapse === false) {
        // 收起节点
        const hasEdges = this.graphData?.edges.map((edge: any) => {
          return `${this.baseUri}#${edge.source}${this.baseUri}#${edge.target}`
        })
        const formData = new FormData()
        formData.append('knowledgeGraphFileId', this.graphId.toString())
        formData.append(
          'vertxName',
          window.btoa(encodeURIComponent(`${this.baseUri}#${nodeData.id}`))
        )
        formData.append('hasEdges', JSON.stringify(hasEdges))
        const response = await collapseVertice({
          data: formData,
        })
        const { data } = response.data
        const shapeData = shapePrefixInfo(data, this.baseUri)
        removeAction(nodeData, shapeData.verticesToDrop, shapeData.edgesToDrop)
      }
      const currentNode = this.graph.findById(nodeData.id)
      const model = currentNode.getModel()
      model.collapse = !nodeData.collapse
      currentNode.update(model)
      // 收起展开应该对应右键的关系过滤
      if (this.rightClickDict[nodeData.id] !== undefined) {
        Object.keys(this.rightClickDict[nodeData.id].rightClick).forEach(
          (key: any) => {
            // 菜单不会动态更新，所以展开折叠操作之后，右键菜单选中样式没变
            this.rightClickDict[nodeData.id].rightClick[key] =
              !nodeData.collapse
          }
        )
      }
    }

    const resetNodeSelected = () => {
      this.currentPage = 1
      this.totalPages = 0
      this.isNodeTableLoading = false
      this.selectedNodeModel = {}
      this.selectedNodeData = {}
      this.selectedNodeBasic = { text: [], image: [] }
      this.nodeEditTableList = []
      this.nodeEditTableIndex = -1
      this.nodeTableColumns = []
      this.nodeTableData = []
      this.chartsGallery = {}
      this.widgets = []
    }

    const onNodeSelectChange = async (event: any) => {
      console.log('onNodeSelectChange:', event.selectedItems)
      // console.log('onNodeSelectChange', event)
      if (event.selectedItems.nodes[0] !== undefined) {
        //  set selectedNode for gisMap focus
        const { id, attrs } = event.selectedItems.nodes[0]._cfg.model
        if ('纬度' in attrs) {
          KGraphStore.setSelectedEntityNode({
            id,
            attrs,
          })
        }

        resetNodeSelected()
        this.nodeEditPanelVisible = true
        setTimeout(() => {
          this.initNotGisPanelViewEvent()
        }, 500)
        this.selectedNodeModel = event.selectedItems.nodes[0]._cfg.model
        // zoom到选中节点
        this.zoomToTargetNode(this.selectedNodeModel.id, 200)
        // 节点有重叠的时候，选中节点设置为最上层
        const choosenNode = this.graph.findById(this.selectedNodeModel.id)
        choosenNode.toFront()

        const response = await queryPanelInfoByVid({
          params: {
            objectName: window.btoa(
              encodeURIComponent(
                `${this.baseUri}#${this.selectedNodeModel.name}`
              )
            ),
            knowledgeGraphFileId: this.graphId,
          },
        })
        const { code, data } = response.data
        const shapeData = shapePrefixInfo(data, this.baseUri)
        this.selectedNodeData = shapeData
        if (code === 200) {
          // basic 属性
          const attributeItems = shapeData.items.find((item: any) => {
            return item.name === '属性'
          })
          await attributeItems.children.forEach((item: any) => {
            if (['text', 'link', 'email'].includes(item.type)) {
              // owl文件中，img的类型也是string
              if (item.attrValue.includes('data:image/')) {
                this.selectedNodeBasic.image.push(item)
              } else {
                this.selectedNodeBasic.text.push(item)
              }
            } else if (item.type === 'table') {
              this.chartsGallery[item.attrValue] = []
              this.nodeEditTableList.push(item)
            } else if (item.type === 'img') {
              this.selectedNodeBasic.image.push(item)
            } else if (item.type === 'chart') {
              const widget = JSON.parse(item.config)
              const { chartType } = widget
              const chartItem = {
                id: item.widgetId,
                chartType,
                imgSrc: this.chartMaps[chartType[0]]?.filter(
                  (chartConfig: any) => chartConfig.name === chartType[1]
                )[0]?.imgSrc,
                data: widget,
                tableName: item.tableName,
              }
              this.chartsGallery[item.tableName].push(chartItem)
              // const galleryIndex = this.chartsGallery[item.tableName].length
              // const query = {
              //   data: {
              //     tableName: item.tableName,
              //     type: 'dataset',
              //     tableJson: {
              //       curPage: this.currentPage,
              //       pageSize: 13,
              //       name: '_record_id_',
              //       filters: [],
              //       sortCol: [],
              //     },
              //   },
              // }
              // getData(query).then((widgetDataResponse: any) => {
              //   this.gridItemSyncBindData({
              //     data: {
              //       query,
              //       data: parseGetDataResult(
              //         response.data?.result || {
              //           columns: [],
              //           data: [],
              //           extraData: {},
              //         },
              //         chartType,
              //         item
              //       ),
              //       columns: item.columns,
              //     },
              //     item,
              //     galleryIndex,
              //   })
              // })
            }
          })
          // basic 关系
          const edgeItem = shapeData.items.find((item: any) => {
            return item.name === '关系'
          })
          await edgeItem.children.forEach((item: any) => {
            if (item.type === 'text') {
              this.selectedNodeBasic.text.push(item)
            }
          })

          console.log('this.selectedNodeBasic:', this.selectedNodeBasic)

          if (this.nodeEditTableList.length !== 0) {
            this.nodeEditTableIndex = 0
            this.getTableData(this.nodeEditTableList[0].attrValue)
          }
          this.$forceUpdate()
        }
      } else {
        this.nodeEditPanelVisible = false
        resetNodeSelected()
        this.$forceUpdate()
      }
    }

    const debounceNodeSelectChange = debounce((event: any) => {
      onNodeSelectChange(event)
    }, 200)

    this.graph.on('nodeselectchange', (event: any) => {
      console.log('nodeselectchange:', event, event.select, this.rightClickDict)
      // 没选中任何节点的情况下，this.rightClickDict中所有的click初始化为false
      if (!event.select) {
        this.initRightClickDict()
        this.handleHighLight(this.graph.save(), '')
      } else {
        // 选中某个节点，则将其他节点的菜单选中情况全部初始化为false
        this.initRightClickDict(event.selectedItems.nodes[0]._cfg.model.id)
      }
      debounceNodeSelectChange(event)
    })

    this.graph.on('afterlayout', () => {
      this.graph.fitView(100)
      this.isGraphVisLoading = false
      // 迭代布局完成之后，记录数据位置信息
      this.graphDataAfterLayout = this.graph.save()
    })

    // 单击收起按钮
    this.graph.on('collapse-icon:click', (event: any) => {
      this.nodeEditPanelVisible = false
      resetNodeSelected()
      event.propagationStopped = true
      const model = event.item.getModel()
      changeCollapse(model)
    })

    // 双击节点
    this.graph.on('node:dblclick', (event: any) => {
      console.log('node:dblclick')
      const model = event.item.getModel()
      changeCollapse(model)
    })

    const setContextMenuResult = async (event: any) => {
      contextMenuResult = ''
      const eventId = event.item.getModel().id
      if (this.rightClickDict[eventId] === undefined) {
        const response = await queryPanelInfoByVid({
          params: {
            objectName: window.btoa(
              encodeURIComponent(`${this.baseUri}#${eventId}`)
            ),
            knowledgeGraphFileId: this.graphId,
          },
        })
        const { code, data } = response.data
        if (code === 200) {
          this.rightClickDict[eventId] = {
            rightClick: shapePrefixInfo(data.rightClick, this.baseUri),
            idsByELabel: shapePrefixInfo(data.idsByELabel, this.baseUri),
          }
          const rightClickMenuItemList: any = Object.keys(
            this.rightClickDict[eventId].rightClick
          )
          let temporaryResult = ''
          rightClickMenuItemList.forEach((item: any) => {
            // this.rightClick初始化都应该为false，操作都留到页面上
            this.rightClickDict[eventId].rightClick[item] = false
            temporaryResult += `<div id="${item}" value="${item}" style="padding: 2px 6px; margin:2px 0;">${item}</div>`
          })
          contextMenuResult = `<div style="margin: auto">${temporaryResult}</div>`
        }
      } else {
        const rightClickMenuItemList: any = Object.keys(
          this.rightClickDict[eventId].rightClick
        )
        let temporaryResult = ''
        if (rightClickMenuItemList.length === 0) {
          contextMenuResult = '<div style="margin: auto">节点出度为零</div>'
        } else {
          rightClickMenuItemList.forEach((item: any) => {
            // console.log(this.rightClickDict[eventId].rightClick[item])
            temporaryResult += `${
              this.rightClickDict[eventId].rightClick[item]
                ? `<div id="${item}" value="${item}" style="background-color: #f0f0f0; padding: 2px 6px; margin:2px 0;">${item}</div>`
                : `<div id="${item}" value="${item}" style="padding: 2px 6px; margin:2px 0;">${item}</div>`
            }
          `
          })
          contextMenuResult = `<div style="margin: auto">${temporaryResult}</div>`
        }
      }
    }

    const debounceSetContextMenuResult = debounce((event: any) => {
      setContextMenuResult(event)
    }, 300)

    // hover节点 暂时用来获取节点右键菜单
    this.graph.on('node:mouseenter', (event: any) => {
      contextMenuResult = ''
      debounceSetContextMenuResult(event)
    })

    // 右键双击画布
    this.graph.on('canvas:dblclick', () => {
      this.graph.fitView(20)
    })

    this.graph.setMinZoom(0.001)

    // 有点问题 用这个好像和g6冲突
    // const rootErd = elementResizeDetectorMaker()
    // rootErd.listenTo(container, () => {
    //   this.graph.changeSize(container.clientWidth, container.clientHeight)
    // })
    // this.$once('hook:beforeDestroy', () => {
    //   rootErd.removeAllListeners(container)
    // })

    if (typeof window !== 'undefined') {
      window.addEventListener('onresize', () => {
        if (!this.graph || this.graph.get('destroyed')) {
          return
        }
        if (!container || !container.scrollWidth || !container.scrollHeight) {
          return
        }
        this.graph.changeSize(container.scrollWidth, container.scrollHeight)
      })
    }
  }
}
</script>

<style lang="less" scoped>
/deep/ .ant-kg-appl-table-thead > tr > th {
  background-color: #fff;
}

.graph-vis-content {
  .graph-vis {
    height: calc(100vh - 104px);
  }

  .node-selected-graph-vis {
    height: calc(100vh - 504px);
  }

  .header {
    border-bottom: 1px solid #e9e9e9;
    display: flex;
    justify-content: space-between;
    padding: 0 16px;
    position: relative;

    .left-header {
      display: flex;

      .header-text {
        color: #5d637e;
        font-weight: 500;
        letter-spacing: 1px;
        margin: auto 0;
        white-space: nowrap;
      }

      .split-line {
        background: #999;
        height: 14px;
        margin: 12px 8px 0;
        width: 1px;
      }

      div {
        display: flex;
        justify-content: space-between;
      }

      .load-button {
        margin: auto 0;
      }

      .tool-icon {
        cursor: pointer;
        font-size: 16px;
        height: 26px;
        margin: 5px;
      }

      .graph-text {
        margin: auto 0;
      }

      .selected-text {
        color: #6973ff;
      }

      .delete-button {
        margin: auto 0;
        padding: 0;
      }
    }

    .toolbar {
      background-color: #fff;
      display: flex;
      flex-direction: row-reverse;
      height: 38px;
      line-height: 38px;
      position: relative;
      transition: width 0.2s ease;
      vertical-align: middle;

      .tool-icon {
        cursor: pointer;
        font-size: 16px;
        height: 26px;
        margin: 0 2px;
        padding: 4px;
      }

      .btn-select {
        background-color: rgba(105, 115, 255, 0.1);
        color: #6973ff;
      }

      .split-line {
        background: #999;
        height: 14px;
        margin: 10px 8px;
        width: 1px;
      }

      /deep/ .ant-kg-appl-btn.ant-kg-appl-btn-link {
        color: #5d637e;

        &:hover .tool-icon {
          background-color: #f1f1f1;
          color: #5561ff !important;
        }
      }

      .detail-menu {
        border-radius: 2px;
        box-shadow: 0 0 4px 0 rgba(88, 98, 230, 0.2);
        position: absolute;
        top: 38px;
      }
    }
  }
}

.vis-search {
  align-items: center;
  background: #fff;
  border-radius: 2px;
  box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.18);
  display: flex;
  height: 48px;
  justify-content: space-between;
  padding: 0 12px;
  position: absolute;
  top: 1px;
  width: 235px;

  // 搜索框圆角
  /deep/ .ant-kg-appl-input-affix-wrapper {
    .ant-kg-appl-input-suffix {
      right: 0;
    }
  }

  .close-icon {
    align-items: center;
    border-radius: 2px;
    color: rgb(93, 99, 126);
    cursor: pointer;
    display: flex;
    font-size: 16px;
    padding: 4px;
    position: relative;
    user-select: none;
  }
}

.node-edit-panel {
  height: 400px;
  position: relative;
  user-select: none;
  width: 100%;

  .gis-panel {
    display: flex;
    flex-direction: column;
    height: 400px;
  }

  .not-gis-panel {
    display: flex;

    .hover-visible {
      background-color: rgba(127, 127, 127, 0.5);
      bottom: 0;
      left: 0;
      position: absolute;
      right: 0;
      top: 0;
      visibility: hidden;
    }

    .widget-name {
      color: #fff;
      overflow: hidden;
      padding: 5px;
      position: relative;
      text-align: center;
      text-overflow: ellipsis;
      top: 12px;
      white-space: nowrap;
    }

    .delete-icon {
      color: #fff;
      font-size: 13px;
      position: absolute;
      right: 3px;
      top: 0.5px;
    }

    .edit-icon {
      color: #fff;
      font-size: 14px;
      position: absolute;
      right: 20px;
      top: 0;
    }

    .widget-item:hover > .hover-visible {
      visibility: visible;
    }

    .widget-item {
      padding: 10px;
      position: relative;
    }
  }

  .node-edit-header {
    background: #eff0f7;
    border-left: 1px solid #e9e9e9;
    border-right: 1px solid #e9e9e9;
    flex: 0 0 38px;
    font-size: 14px;
    font-weight: 600;
    height: 38px;
    line-height: 38px;
    padding: 0 16px;
    position: relative;
  }

  .left-header-fold {
    align-items: center;
    background-color: #eff0f7;
    border-bottom: 0;
    display: flex;
    font-size: 14px;
    font-weight: 600;
    height: 400px;
    height: auto;
    letter-spacing: 8px;
    padding: 10px;
    width: 44px;
    writing-mode: vertical-lr;

    div {
      padding: auto;
    }

    .icon {
      left: 11px;
    }
  }

  .left-header {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .right-header {
    display: flex;
    flex-direction: row;
    justify-content: space-between;

    .add-vis-button {
      margin: auto 0;
    }
  }

  .node-edit-body {
    background: #fafafc;
    border: 1px solid #e9e9e9;
    border-bottom-width: 0;
    border-top-width: 0;
    // display: flex;
    height: 360px;
    overflow-x: auto;
    overflow-y: auto;
    padding: 8px 9px;

    /* 滚动条 */

    &::-webkit-scrollbar {
      height: 10px;
      width: 6px;
    }

    &::-webkit-scrollbar-track {
      background: rgb(239, 239, 239);
      border-radius: 2px;
    }

    &::-webkit-scrollbar-thumb {
      background: #bfbfbf;
      border-radius: 6px;
    }

    &::-webkit-scrollbar-thumb:hover {
      background: #333;
    }

    &::-webkit-scrollbar-corner {
      background: #179a16;
    }

    .default-item {
      color: rgba(34, 36, 50, 0.5);
      cursor: pointer;
    }

    .default-item.select-item {
      color: #6973ff;
      cursor: auto;
    }

    .table-container {
      position: relative;

      .loading-mask,
      .no-data {
        align-content: center;
        align-items: center;
        background: transparent;
        display: flex;
        height: 100%;
        justify-content: center;
        position: absolute;
        text-align: center;
        transform: translateY(-40px);
        width: 100%;
        z-index: 100;

        .loading-status {
          align-content: center;
          align-items: center;
          bottom: 0;
          color: var(--title-color);
          display: flex;
          flex-flow: row nowrap;
          font-size: 12px;
          font-weight: 500;
          justify-content: center;
          left: 0;
          letter-spacing: 1px;
          line-height: 36px;
          position: absolute;
          right: 0;
          top: 0;
          vertical-align: middle;
          z-index: 1000;

          .loading-text {
            display: inline-block;
            padding-left: 4px;
          }

          .sync-icon {
            color: #5760e6;
            font-size: 24px;
          }
        }
      }

      .no-data-img {
        left: 50%;
        position: absolute;
        transform: translateX(-50%);
      }
    }
  }

  /* 改变节点编辑模块高度 */

  .resize-edit-panel-height {
    border-color: transparent;
    height: 10px;
    position: absolute;
    top: 0;
    width: 100%;

    &:hover {
      cursor: ns-resize;
    }
  }

  .node-edit-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .item-option {
    display: flex;
    justify-content: space-between;
  }

  .media-container {
    display: flex;
    height: 160px;
    justify-content: center;
    margin: auto;
  }

  /deep/ .ant-kg-appl-list-items {
    display: flex;
    flex-flow: wrap;
  }

  /deep/ .data-cell {
    display: block;
    height: 100%;
    padding: 6px 10px;
    position: relative;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 100%;

    .cell-content {
      display: block;
      height: auto;
      overflow: hidden;
      pointer-events: none;
      position: relative;
      text-overflow: ellipsis;

      &.null {
        color: transparent;
      }
    }

    .cell-common {
      display: block;
      height: auto;
      overflow: hidden;
      padding: 0 8px;
      pointer-events: all;
      position: relative;
      text-overflow: ellipsis;
      user-select: text;
    }

    .cell-hilight {
      bottom: 0;
      left: 0;
      overflow: hidden;
      padding: 6px 10px;
      pointer-events: all;
      position: absolute;
      right: 0;
      text-overflow: ellipsis;
      top: 0;
      user-select: text;
      white-space: nowrap;
      z-index: 0;
    }
  }

  .node-panel-resize-line {
    background-color: transparent;
    cursor: col-resize;
    min-height: 100%;
    position: absolute;
    user-select: auto;
    width: 10px;
    z-index: 1;
  }

  .simple-virtual-table {
    overflow-x: hidden;
  }

  .node-panel-text-warpper {
    display: flex;
    margin-bottom: 8px;
    // line-height: 30px;

    .node-panel-text-name {
      flex-shrink: 0;
      padding: 0 8px 0 0;
      width: 65px;
      word-break: break-all;
    }

    .entity-link {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: pre-line;
    }

    .node-panel-text-value {
      user-select: text;
      word-wrap: break-word;
    }
  }
}

.btn-select {
  background-color: rgba(105, 115, 255, 0.1);
  color: #6973ff;
}

// expand collapse start
.wrapper {
  display: flex;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: pre-line;
  width: 100%;
}

.text {
  line-height: 1.5;
  max-height: 7.5em;
  overflow: hidden;
  position: relative;
  text-align: justify;
  text-overflow: ellipsis;
  width: 100%;
}

.text::before {
  content: '';
  float: right;
  height: calc(100% - 20px);
}

.hide-btn .btn {
  display: none;
}

// .hide-btn .content-value {
//   overflow: hidden;
//   width: 80%;
// }

.text::after {
  box-shadow: inset calc(100px - 999vw) calc(30px - 999vw) 0 0 #f9f9fc;
  content: '';
  height: 999vw;
  margin-left: -100px;
  position: absolute;
  width: 999vw;
}

.btn {
  background: #6973ff;
  border-radius: 4px;
  clear: both;
  color: #fff;
  cursor: pointer;
  float: right;
  font-size: 12px;
  line-height: 13px;
  margin-left: 20px;
  padding: 0 6px;
  position: relative;
  top: 7px;
}

.btn::after {
  content: '展开';
}

.exp {
  display: none;
}

.exp:checked + .text {
  max-height: none;
}

.exp:checked + .text::after {
  visibility: hidden;
}

.exp:checked + .text .btn::before {
  visibility: hidden;
}

.exp:checked + .text .btn::after {
  content: '收起';
}

.btn::before {
  color: #333;
  content: '...';
  left: -5px;
  position: absolute;
  transform: translateX(-100%);
}
// expand collapse end

.guest-mode {
  user-select: none;

  /deep/ .ant-kg-appl-btn {
    cursor: not-allowed;
    opacity: 0.45;
  }
}
</style>
